Named 추가, WebClient multipart request 수정

master
mjkhan21 10 months ago
parent 3335571561
commit e84b5c65de

@ -0,0 +1,41 @@
package cokr.xit.foundation.data;
/**
* @author mjkhan
*/
public class Named<T> {
private String name;
private T value;
/** .
* @return
*/
public String getName() {
return name;
}
/** .
* @param name
* @return Named
*/
public Named<T> setName(String name) {
this.name = name;
return this;
}
/** .
* @return
*/
public T getValue() {
return value;
}
/** .
* @param value
* @return Named
*/
public Named<T> setValue(T value) {
this.value = value;
return this;
}
}

@ -1,5 +1,6 @@
package cokr.xit.foundation.web;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.math.BigInteger;
@ -7,6 +8,7 @@ import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
@ -29,9 +31,9 @@ import java.util.function.Consumer;
import javax.net.ssl.SSLContext;
import cokr.xit.foundation.Assert;
import cokr.xit.foundation.Log;
import cokr.xit.foundation.AbstractComponent;
import cokr.xit.foundation.data.JSON;
import cokr.xit.foundation.data.Named;
/** http .
* <p>WebClient {@link #get(Consumer) GET}, {@link #post(Consumer) POST} . {@link Request }
@ -83,7 +85,7 @@ import cokr.xit.foundation.data.JSON;
* </ul>
* @author mjkhan
*/
public class WebClient {
public class WebClient extends AbstractComponent {
/**http .
*/
public static void debug() {
@ -147,7 +149,7 @@ public class WebClient {
}
public Charset charset() {
return Assert.ifEmpty(charset, StandardCharsets.UTF_8);
return ifEmpty(charset, StandardCharsets.UTF_8);
}
/** . UTF-8.
@ -216,10 +218,10 @@ public class WebClient {
return (HttpResponse<T>)handleRequest(req.async, hreq, HttpResponse.BodyHandlers.ofString(), req.textHandler);
} catch (Throwable e) {
if (req.async) {
req.errorHandler.accept(Assert.rootCause(e));
req.errorHandler.accept(rootCause(e));
return null;
} else
throw Assert.runtimeException(e);
throw runtimeException(e);
}
}
@ -240,10 +242,6 @@ public class WebClient {
}
}
private static Log log() {
return Log.get(WebClient.class);
}
/**"POST" .
* @param <T> body
* @param configurer "POST"
@ -263,10 +261,10 @@ public class WebClient {
return (HttpResponse<T>)handleRequest(req.async, hreq, HttpResponse.BodyHandlers.ofString(), req.textHandler);
} catch (Throwable e) {
if (req.async) {
req.errorHandler.accept(Assert.rootCause(e));
req.errorHandler.accept(rootCause(e));
return null;
} else
throw Assert.runtimeException(e);
throw runtimeException(e);
}
}
@ -281,7 +279,7 @@ public class WebClient {
) {
inputStream.transferTo(out);
} catch (Exception e) {
throw Assert.runtimeException(e);
throw runtimeException(e);
}
}
@ -289,7 +287,7 @@ public class WebClient {
try {
return new String(inputStream.readAllBytes(), charset());
} catch (Exception e) {
throw Assert.runtimeException(e);
throw runtimeException(e);
}
}
@ -327,7 +325,7 @@ public class WebClient {
}
public static ContentType typeOf(String type) {
if (Assert.isEmpty(type)) return null;
if (isEmpty(type)) return null;
for (ContentType content: values())
if (type.contains(content.type))
@ -348,9 +346,9 @@ public class WebClient {
private JSON json;
private Consumer<HttpResponse<String>> textHandler = hresp -> {
HttpHeaders headers = hresp.headers();
headers.map().forEach((k, v) -> log().debug("{} = {}", k, v));
log().debug("status: {}", hresp.statusCode());
log().debug("{}", hresp.body());
headers.map().forEach((k, v) -> log(WebClient.class).debug("{} = {}", k, v));
log(WebClient.class).debug("status: {}", hresp.statusCode());
log(WebClient.class).debug("{}", hresp.body());
};
private Consumer<HttpResponse<InputStream>> downloadHandler = hresp -> {};
private Consumer<Throwable> errorHandler = t -> {
@ -374,7 +372,7 @@ public class WebClient {
* @return Request
*/
public Request header(Map<String, String> map) {
if (!Assert.isEmpty(map))
if (!isEmpty(map))
map.forEach(this::header);
return this;
}
@ -435,7 +433,7 @@ public class WebClient {
}
private JSON json() {
return Assert.ifEmpty(json, () -> json = new JSON());
return ifEmpty(json, () -> json = new JSON());
}
/** . false().
@ -546,12 +544,12 @@ public class WebClient {
private String inJSON() {
Object body = bodyData();
String str = "";
if (!Assert.isEmpty(body))
if (!isEmpty(body))
str = json().stringify(body);
else if (!Assert.isEmpty(keyValues))
else if (!isEmpty(keyValues))
str = json().stringify(keyValues);
log().debug("json request:\n{}", str);
log(WebClient.class).debug("json request:\n{}", str);
return str;
}
@ -570,7 +568,7 @@ public class WebClient {
return builder.build();
} catch (Exception e) {
throw Assert.runtimeException(e);
throw runtimeException(e);
}
}
@ -590,26 +588,77 @@ public class WebClient {
}
private BodyPublisher multipartPublisher() throws Exception {
String boundary = new BigInteger(64, new Random()).toString();
byte[] separator = ("--" + boundary + "\r\nContent-Disposition: form-data; name=").getBytes(charset);
String boundary = "==xwc-" + new BigInteger(64, new Random()) + "==";
header("Content-Type", ContentType.MULTIPART.type + "; boundary=" + boundary);
byte[] separator = line("--" + boundary);
ArrayList<byte[]> byteList = new ArrayList<>();
for (Map.Entry<String, Object> entry: keyValues.entrySet()) {
byteList.add(separator);
String key = entry.getKey();
Object value = entry.getValue();
if (value instanceof Path path) {
String mimeType = Files.probeContentType(path);
byteList.add(("\"" + entry.getKey() + "\"; filename=\"" + path.getFileName() + "\"\r\nContent-Type: " + mimeType + "\r\n\r\n").getBytes(charset));
byteList.add(Files.readAllBytes(path));
byteList.add("\r\n".getBytes(charset));
if (value instanceof Iterable<?> list) {
for (Object obj: list) {
byteList.addAll(readBytes(separator, key, obj));
}
} else {
byteList.add(("\"" + entry.getKey() + "\"\r\n\r\n" + entry.getValue() + "\r\n").getBytes(charset));
byteList.addAll(readBytes(separator, key, value));
}
}
byteList.add(("--" + boundary + "--\r\n").getBytes(charset));
byteList.add(line("--" + boundary + "--"));
byteList.add(line(""));
// byteList.forEach(bytes -> System.out.print(new String(bytes)));
return BodyPublishers.ofByteArrays(byteList);
}
private byte[] line(String str) {
return (str + "\r\n").getBytes(charset);
}
private List<byte[]> readBytes(byte[] separator, String key, Path path) throws Exception {
String mimeType = Files.probeContentType(path);
return List.of(
separator,
line("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + path.getFileName() + "\""),
line("Content-Type: " + mimeType),
line("Content-Transfer-Encodig: binary"),
line(""),
Files.readAllBytes(path),
line(""),
line("")
);
}
private List<byte[]> readBytes(byte[] separator, String key, String filename, InputStream inputStream) throws Exception {
String mimeType = URLConnection.guessContentTypeFromStream(inputStream);
return List.of(
separator,
line("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + filename + "\""),
line("Content-Type: " + mimeType),
line("Content-Transfer-Encodig: binary"),
line(""),
inputStream.readAllBytes(),
line(""),
line("")
);
}
private List<byte[]> readBytes(byte[] separator, String key, Object obj) throws Exception {
if (obj instanceof Path path)
return readBytes(separator, key, path);
else if (obj instanceof File file)
return readBytes(separator, key, file.toPath());
else if (obj instanceof Named<?> named)
return readBytes(separator, key, named.getName(), (InputStream)named.getValue());
else
return List.of(
separator,
line("Content-Disposition: form-data; name=\"" + key + "\""),
line("Content-Type: text/plain; charset=" + charset),
line(""),
line(blankIfEmpty(obj))
);
}
}
}
Loading…
Cancel
Save