diff --git a/scx-common/src/main/java/cool/scx/common/util/FileUtils.java b/scx-common/src/main/java/cool/scx/common/util/FileUtils.java index 6c84a2c9..e1a73405 100644 --- a/scx-common/src/main/java/cool/scx/common/util/FileUtils.java +++ b/scx-common/src/main/java/cool/scx/common/util/FileUtils.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.file.*; @@ -214,4 +215,17 @@ public static void merge(Path source, Path target) throws IOException { } } + public static void appendToFile(Path target, InputStream appendContent) throws IOException { + try (var in = appendContent) { + try (var out = Files.newOutputStream(target, APPEND, CREATE, SYNC, WRITE)) { + in.transferTo(out); + } catch (NoSuchFileException e) { + Files.createDirectories(target.getParent()); + try (var out = Files.newOutputStream(target, APPEND, CREATE, SYNC, WRITE);) { + in.transferTo(out); + } + } + } + } + } diff --git a/scx-core/src/test/java/cool/scx/core/test/TestModule.java b/scx-core/src/test/java/cool/scx/core/test/TestModule.java index 79b82eb6..1d544051 100644 --- a/scx-core/src/test/java/cool/scx/core/test/TestModule.java +++ b/scx-core/src/test/java/cool/scx/core/test/TestModule.java @@ -16,8 +16,9 @@ import cool.scx.core.test.person.Person; import cool.scx.core.test.person.PersonService; import cool.scx.data.query.QueryOption; +import cool.scx.http.routing.handler.StaticHandler; import cool.scx.jdbc.sql.SQL; -//import org.springframework.scheduling.support.CronTrigger; +import cool.scx.scheduling.ScxScheduling; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -32,12 +33,9 @@ import java.util.Arrays; import java.util.List; -import static cool.scx.core.eventbus.ZeroCopyMessageCodec.ZERO_COPY_CODEC_NAME; -import static cool.scx.core.eventbus.ZeroCopyMessageWrapper.zeroCopyMessage; import static cool.scx.data.field_filter.FieldFilterBuilder.ofIncluded; import static cool.scx.data.query.QueryBuilder.*; import static java.lang.System.Logger.Level.ERROR; -import static org.testng.Assert.assertEquals; public class TestModule extends ScxModule { @@ -151,7 +149,6 @@ public static void test1() throws SocketException { //测试 URIBuilder for (int i = 0; i < 1000; i = i + 1) { var s = "http://" + ip.getHostAddress() + ":8888/test0"; - // todo // try { // var stringHttpResponse = ScxHttpClientHelper.post( // URIBuilder.of(s) @@ -179,11 +176,11 @@ public static void test2() { // ScxContext.eventBus().request("test-event-bus", car, new DeliveryOptions().setCodecName(ZERO_COPY_CODEC_NAME), c -> { // assertEquals(c.result().body(), car); // }); -// 通过指定 ZERO_COPY_CODEC_NAME 实现 0 拷贝 +// //通过指定 ZERO_COPY_CODEC_NAME 实现 0 拷贝 // ScxContext.eventBus().send("test-event-bus", car, new DeliveryOptions().setCodecName(ZERO_COPY_CODEC_NAME)); -// 通过 @ZeroCopyMessage 注解实现 零拷贝 +// //通过 @ZeroCopyMessage 注解实现 零拷贝 // ScxContext.eventBus().publish("test-event-bus", car); -// 通过 zeroCopyMessage() 包装器实现 零拷贝 (会自动脱壳) +// //通过 zeroCopyMessage() 包装器实现 零拷贝 (会自动脱壳) // ScxContext.eventBus().send("test-event-bus", zeroCopyMessage(car)); } @@ -244,30 +241,38 @@ public static void test4() { */ @Override public void start(Scx scx) { - //todo -// scx.scxHttpRouter().route("/static/*") -// .handler(StaticHandler.create(FileSystemAccess.ROOT, scx.scxEnvironment().getPathByAppRoot("AppRoot:c\\static").toString()) -// .setFilesReadOnly(false)); -// var logger = System.getLogger(TestModule.class.getName()); -// //测试定时任务 -// scx.scxScheduler().scheduleAtFixedRate((a) -> { -// //测试 -// logger.log(ERROR, "这是通过 ScxContext.scheduleAtFixedRate() 打印的 : 一共 10 次 , 这时第 " + a.runCount() + " 次执行 !!!"); -// }, Instant.now().plusSeconds(3), Duration.of(1, ChronoUnit.SECONDS), 10); -// -// scx.scxScheduler().schedule((a) -> { -// //测试 -// logger.log(ERROR, "这是通过 ScxContext.scheduler() 使用 Cron 表达式 打印的 : 这时第 " + a.runCount() + " 次执行 !!!"); -// }, new CronTrigger("*/1 * * * * ?")); -// -// scx.scxScheduler().scheduleAtFixedRate((a) -> { -// logger.log(ERROR, "这是通过 ScxContext.scheduleAtFixedRate() 打印的 : 不限次数 不过到 第 10 次手动取消 , 这是第 " + a.runCount() + " 次执行 !!!"); -// if (a.runCount() >= 10) { -// a.scheduledFuture().cancel(false); -// } -// }, Instant.now().plusSeconds(3), Duration.of(1, ChronoUnit.SECONDS)); -// -// System.out.println("CarModule-Start"); + scx.scxHttpRouter().route() + .path("/static/*") + .handler(new StaticHandler(scx.scxEnvironment().getPathByAppRoot("AppRoot:c\\static"))); + var logger = System.getLogger(TestModule.class.getName()); + //测试定时任务 + ScxScheduling.fixedRate() + .startTime(Instant.now().plusSeconds(3)) + .delay(Duration.of(1, ChronoUnit.SECONDS)) + .maxRunCount(10) + .start((a) -> { + //测试 + logger.log(ERROR, "这是通过 ScxContext.scheduleAtFixedRate() 打印的 : 一共 10 次 , 这时第 " + a.runCount() + " 次执行 !!!"); + }); + + ScxScheduling.cron() + .expression("*/1 * * * * ?") + .start((a) -> { + //测试 + logger.log(ERROR, "这是通过 ScxContext.scheduler() 使用 Cron 表达式 打印的 : 这时第 " + a.runCount() + " 次执行 !!!"); + }); + + ScxScheduling.fixedRate() + .startTime(Instant.now().plusSeconds(3)) + .delay(Duration.of(1, ChronoUnit.SECONDS)) + .start((a) -> { + logger.log(ERROR, "这是通过 ScxContext.scheduleAtFixedRate() 打印的 : 不限次数 不过到 第 10 次手动取消 , 这是第 " + a.runCount() + " 次执行 !!!"); + if (a.runCount() >= 10) { + a.cancel(); + } + }); + + System.out.println("CarModule-Start"); } } diff --git a/scx-core/src/test/java/cool/scx/core/test/website/WebSiteController.java b/scx-core/src/test/java/cool/scx/core/test/website/WebSiteController.java index 3e784397..64485d4b 100644 --- a/scx-core/src/test/java/cool/scx/core/test/website/WebSiteController.java +++ b/scx-core/src/test/java/cool/scx/core/test/website/WebSiteController.java @@ -59,8 +59,8 @@ public static Object test0(@FromQuery String name, return Map.of("now", yyyy_MM_dd_HH_mm_ss.format(LocalDateTime.now()), "name", name, "age", age, - "content", new String(content.content(), StandardCharsets.UTF_8), - "content1", new String(content1.content(), StandardCharsets.UTF_8)); + "content", content.asString(), + "content1", content.asString()); } @ScxRoute(methods = HttpMethod.GET) diff --git a/scx-ext/src/main/java/cool/scx/ext/fss/FSSController.java b/scx-ext/src/main/java/cool/scx/ext/fss/FSSController.java index a656610a..eac2c553 100644 --- a/scx-ext/src/main/java/cool/scx/ext/fss/FSSController.java +++ b/scx-ext/src/main/java/cool/scx/ext/fss/FSSController.java @@ -5,7 +5,7 @@ import cool.scx.http.HttpMethod; import cool.scx.http.exception.InternalServerErrorException; import cool.scx.http.exception.NotFoundException; -import cool.scx.http.media.multi_part.CachedMultiPartPart; +import cool.scx.http.media.multi_part.MultiPartPart; import cool.scx.web.annotation.*; import cool.scx.web.vo.*; @@ -77,7 +77,7 @@ public BaseVo upload( @FromBody String fileHash,// 文件 Hash @FromBody Integer chunkLength,//分片总长度 @FromBody Integer nowChunkIndex,//当前分片 - @FromUpload CachedMultiPartPart fileData//文件内容 + @FromUpload MultiPartPart fileData//文件内容 ) throws Exception { var uploadTempFile = getUploadTempPath(fileHash).resolve("scx_fss.temp"); var uploadConfigFile = uploadTempFile.resolveSibling("scx_fss.upload_state"); @@ -85,7 +85,7 @@ public BaseVo upload( //判断是否上传的是最后一个分块 (因为 索引是以 0 开头的所以这里 -1) if (nowChunkIndex == chunkLength - 1) { //先将数据写入临时文件中 - FileUtils.merge(uploadTempFile, fileData.contentPath()); + FileUtils.appendToFile(uploadTempFile, fileData.inputStream()); //获取文件描述信息创建 fssObject 对象 var newFSSObject = createFSSObjectByFileInfo(fileName, fileSize, fileHash); //获取文件真实的存储路径 @@ -113,7 +113,7 @@ public BaseVo upload( var needUploadChunkIndex = lastUploadChunk + 1; //当前的区块索引和需要的区块索引相同 就保存文件内容 if (nowChunkIndex.equals(needUploadChunkIndex)) { - FileUtils.merge(uploadTempFile, fileData.contentPath()); + FileUtils.appendToFile(uploadTempFile, fileData.inputStream()); //将当前上传成功的区块索引和总区块长度保存到配置文件中 updateLastUploadChunk(uploadConfigFile, nowChunkIndex, chunkLength); //像前台返回我们需要的下一个区块索引 diff --git a/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHttpClientRequest.java b/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHttpClientRequest.java index 35e80a06..5b47d7aa 100644 --- a/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHttpClientRequest.java +++ b/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHttpClientRequest.java @@ -2,7 +2,8 @@ import cool.scx.http.HttpMethod; import cool.scx.http.ScxHttpClientRequestBase; -import cool.scx.http.ScxHttpClientResponse; +import cool.scx.http.ScxHttpHeaders; +import cool.scx.http.media.MediaWriter; import io.helidon.http.HeaderNames; import io.helidon.http.Method; import io.helidon.webclient.api.WebClient; @@ -20,25 +21,15 @@ public HelidonHttpClientRequest(WebClient webClient) { } @Override - public HelidonHttpClientResponse send() { + public HelidonHttpClientResponse send(MediaWriter writer) { var r = webClient.method(Method.create(method.value())); r.uri(uri.toString()); + writer.beforeWrite(headers, ScxHttpHeaders.of()); for (var h : headers) { r.header(HeaderNames.create(h.getKey().value()), h.getValue()); } - var res = r.request(); - return new HelidonHttpClientResponse(res); - } - - @Override - public ScxHttpClientResponse send(Object body) { - var r = webClient.method(Method.create(method.value())); - r.uri(uri.toString()); - for (var h : headers) { - r.header(HeaderNames.create(h.getKey().value()), h.getValue()); - } - var res = r.submit(body); - return new HelidonHttpClientResponse(res); + var httpClientResponse = r.outputStream(writer::write); + return new HelidonHttpClientResponse(httpClientResponse); } } diff --git a/scx-http/src/main/java/cool/scx/http/HttpHelper.java b/scx-http/src/main/java/cool/scx/http/HttpHelper.java index 3fe73c0b..93202629 100644 --- a/scx-http/src/main/java/cool/scx/http/HttpHelper.java +++ b/scx-http/src/main/java/cool/scx/http/HttpHelper.java @@ -54,12 +54,12 @@ public static String getDownloadContentDisposition(String downloadName) { return "attachment; filename*=utf-8''" + encode(downloadName, UTF_8).replace("+", "%20"); } - public static MediaType getMediaTypeFromExtension(String ext) { + public static MediaType getMediaTypeByExtension(String ext) { var fileFormat = FileFormat.ofExtension(ext); return fileFormat != null ? fileFormat.mediaType() : null; } - public static MediaType getMediaTypeFromFileName(String filename) { + public static MediaType getMediaTypeByFileName(String filename) { var fileFormat = FileFormat.ofFileName(filename); return fileFormat != null ? fileFormat.mediaType() : null; } diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java b/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java index d0bbbcaf..b4e923db 100644 --- a/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java +++ b/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java @@ -2,9 +2,8 @@ import cool.scx.http.media.MediaReader; import cool.scx.http.media.form_params.FormParams; -import cool.scx.http.media.multi_part.CachedMultiPart; -import cool.scx.http.media.multi_part.CachedMultiPartReader; import cool.scx.http.media.multi_part.MultiPart; +import cool.scx.http.media.multi_part.MultiPartStreamCachedReader; import cool.scx.http.media.path.PathReader; import cool.scx.http.media.string.StringReader; @@ -15,8 +14,8 @@ import static cool.scx.http.media.byte_array.ByteArrayReader.BYTE_ARRAY_READER; import static cool.scx.http.media.form_params.FormParamsReader.FORM_PARAMS_READER; -import static cool.scx.http.media.multi_part.CachedMultiPartReader.CACHED_MULTI_PART_READER; -import static cool.scx.http.media.multi_part.MultiPartReader.MULTI_PART_READER; +import static cool.scx.http.media.multi_part.MultiPartStreamCachedReader.MULTI_PART_READER_CACHED; +import static cool.scx.http.media.multi_part.MultiPartStreamReader.MULTI_PART_READER; import static cool.scx.http.media.string.StringReader.STRING_READER; /** @@ -48,12 +47,12 @@ default MultiPart asMultiPart() { return as(MULTI_PART_READER); } - default CachedMultiPart asCachedMultiPart() { - return as(CACHED_MULTI_PART_READER); + default MultiPart asMultiPartCached() { + return as(MULTI_PART_READER_CACHED); } - default CachedMultiPart asCachedMultiPart(Path cachePath) { - return as(new CachedMultiPartReader(cachePath)); + default MultiPart asMultiPartCached(Path cachePath) { + return as(new MultiPartStreamCachedReader(cachePath)); } default Path asPath(Path path, OpenOption... options) { diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java b/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java index d9dc8ee4..ec70c65b 100644 --- a/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java +++ b/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java @@ -1,8 +1,19 @@ package cool.scx.http; +import cool.scx.http.media.MediaWriter; +import cool.scx.http.media.byte_array.ByteArrayWriter; +import cool.scx.http.media.input_stream.InputStreamWriter; +import cool.scx.http.media.multi_part.MultiPart; +import cool.scx.http.media.multi_part.MultiPartWriter; +import cool.scx.http.media.path.PathWriter; +import cool.scx.http.media.string.StringWriter; import cool.scx.http.uri.ScxURI; import cool.scx.http.uri.ScxURIWritable; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.file.Path; + /** * ScxHttpClientRequest */ @@ -20,9 +31,39 @@ public interface ScxHttpClientRequest { ScxHttpClientRequest headers(ScxHttpHeadersWritable headers); - ScxHttpClientResponse send(); + ScxHttpClientResponse send(MediaWriter writer); + + default ScxHttpClientResponse send() { + return send(new byte[]{}); + } + + default ScxHttpClientResponse send(byte[] bytes) { + return send(new ByteArrayWriter(bytes)); + } + + default ScxHttpClientResponse send(String str) { + return send(new StringWriter(str)); + } + + default ScxHttpClientResponse send(String str, Charset charset) { + return send(new StringWriter(str, charset)); + } + + default ScxHttpClientResponse send(Path path) { + return send(new PathWriter(path)); + } + + default ScxHttpClientResponse send(Path path, long offset, long length) { + return send(new PathWriter(path, offset, length)); + } + + default ScxHttpClientResponse send(InputStream inputStream) { + return send(new InputStreamWriter(inputStream)); + } - ScxHttpClientResponse send(Object body); + default ScxHttpClientResponse send(MultiPart multiPart) { + return send(new MultiPartWriter(multiPart)); + } default ScxHttpClientRequest uri(String uri) { return uri(ScxURI.of(uri)); diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpHeaders.java b/scx-http/src/main/java/cool/scx/http/ScxHttpHeaders.java index e8f1f22e..3a8546dc 100644 --- a/scx-http/src/main/java/cool/scx/http/ScxHttpHeaders.java +++ b/scx-http/src/main/java/cool/scx/http/ScxHttpHeaders.java @@ -65,4 +65,8 @@ default Cookie getSetCookie(String name) { return setCookies().get(name); } + default String encode() { + return ScxHttpHeadersHelper.encodeHeaders(this); + } + } diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpHeadersHelper.java b/scx-http/src/main/java/cool/scx/http/ScxHttpHeadersHelper.java index e50042ad..f0ab9c65 100644 --- a/scx-http/src/main/java/cool/scx/http/ScxHttpHeadersHelper.java +++ b/scx-http/src/main/java/cool/scx/http/ScxHttpHeadersHelper.java @@ -18,4 +18,16 @@ public static ScxHttpHeadersWritable parseHeaders(String headersStr) { return headers; } + public static String encodeHeaders(ScxHttpHeaders headers) { + var sb = new StringBuilder(); + for (var header : headers) { + var key = header.getKey(); + var values = header.getValue(); + for (var value : values) { + sb.append(key.value()).append(": ").append(value).append("\r\n"); + } + } + return sb.toString(); + } + } diff --git a/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDisposition.java b/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDisposition.java index 2a4fe3c3..0b95ba7e 100644 --- a/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDisposition.java +++ b/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDisposition.java @@ -38,8 +38,9 @@ default String readDate() { return params().get("read-date"); } - default String size() { - return params().get("size"); + default Long size() { + var size = params().get("size"); + return size != null ? Long.valueOf(size) : null; } String encode(); diff --git a/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDispositionWritable.java b/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDispositionWritable.java index ce166def..8d2503bb 100644 --- a/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDispositionWritable.java +++ b/scx-http/src/main/java/cool/scx/http/content_disposition/ContentDispositionWritable.java @@ -4,8 +4,25 @@ public interface ContentDispositionWritable extends ContentDisposition { + ParametersWritable params(); + ContentDispositionWritable type(String type); ContentDispositionWritable params(ParametersWritable params); + default ContentDispositionWritable name(String name) { + params().set("name", name); + return this; + } + + default ContentDispositionWritable filename(String filename) { + params().set("filename", filename); + return this; + } + + default ContentDispositionWritable size(long size) { + params().set("size", size + ""); + return this; + } + } diff --git a/scx-http/src/main/java/cool/scx/http/content_type/ContentTypeWritable.java b/scx-http/src/main/java/cool/scx/http/content_type/ContentTypeWritable.java index 0bc85a21..f9f62931 100644 --- a/scx-http/src/main/java/cool/scx/http/content_type/ContentTypeWritable.java +++ b/scx-http/src/main/java/cool/scx/http/content_type/ContentTypeWritable.java @@ -18,4 +18,9 @@ default ContentTypeWritable charset(Charset c) { return this; } + default ContentTypeWritable boundary(String boundary) { + params().set("boundary", boundary); + return this; + } + } diff --git a/scx-http/src/main/java/cool/scx/http/media/MediaReader.java b/scx-http/src/main/java/cool/scx/http/media/MediaReader.java index 32a21261..20e97494 100644 --- a/scx-http/src/main/java/cool/scx/http/media/MediaReader.java +++ b/scx-http/src/main/java/cool/scx/http/media/MediaReader.java @@ -5,7 +5,7 @@ import java.io.InputStream; /** - * 媒体读取器 一般用来从 ServerRequest 或 ClientResponse 中读取数据 + * 读取器 可用于 ServerRequest 和 ClientResponse * * @param */ diff --git a/scx-http/src/main/java/cool/scx/http/media/MediaWriter.java b/scx-http/src/main/java/cool/scx/http/media/MediaWriter.java index 7bfb6bf6..5c994844 100644 --- a/scx-http/src/main/java/cool/scx/http/media/MediaWriter.java +++ b/scx-http/src/main/java/cool/scx/http/media/MediaWriter.java @@ -6,17 +6,17 @@ import java.io.OutputStream; /** - * 写入器 + * 写入器 可用于 ServerResponse 和 ClientRequest */ public interface MediaWriter { /** * 写入内容之前 在这里可以设置 header 头 * - * @param responseHeaders 响应头 - * @param requestHeaders 请求头 + * @param headersWritable 响应头 + * @param headers 请求头 */ - void beforeWrite(ScxHttpHeadersWritable responseHeaders, ScxHttpHeaders requestHeaders); + void beforeWrite(ScxHttpHeadersWritable headersWritable, ScxHttpHeaders headers); /** * 写入内容 diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPartPart.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPartPart.java deleted file mode 100644 index 0c2c2bcb..00000000 --- a/scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPartPart.java +++ /dev/null @@ -1,46 +0,0 @@ -package cool.scx.http.media.multi_part; - -import cool.scx.http.ScxHttpHeadersWritable; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -public class CachedMultiPartPart extends MultiPartPart { - - private final Path contentPath; - private byte[] cacheContent = null; - - public CachedMultiPartPart(ScxHttpHeadersWritable headers, byte[] content, Path contentPath) { - super(headers, content); - this.contentPath = contentPath; - } - - @Override - public byte[] content() { - if (content != null) { - return content; - } - if (cacheContent == null) { - try { - cacheContent = Files.readAllBytes(contentPath); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return cacheContent; - } - - public Path contentPath() { - return contentPath; - } - - public void deleteContentPath() { - try { - Files.delete(contentPath); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPart.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPart.java index 7038650c..5f4e16a7 100644 --- a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPart.java +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPart.java @@ -1,72 +1,17 @@ package cool.scx.http.media.multi_part; -import cool.scx.http.ScxHttpHeaders; -import cool.scx.http.ScxHttpHeadersWritable; -import org.apache.commons.fileupload.FileUploadBase; -import org.apache.commons.fileupload.MultipartStream; +import cool.scx.common.util.RandomUtils; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Iterator; -import java.util.NoSuchElementException; +public interface MultiPart extends Iterable { -public class MultiPart implements Iterable, Iterator { - - private final MultipartStream multipartStream; - private boolean hasNextPart; - - public MultiPart(InputStream inputStream, String boundary) { - var boundaryBytes = boundary.getBytes(); - this.multipartStream = new MultipartStream(inputStream, boundaryBytes, 1024, null); - try { - hasNextPart = multipartStream.skipPreamble(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public static ScxHttpHeadersWritable readToHeaders(MultipartStream multipartStream) throws MultipartStream.MalformedStreamException, FileUploadBase.FileUploadIOException { - var headersStr = multipartStream.readHeaders(); - return ScxHttpHeaders.of(headersStr); + static MultiPartWritable of(String boundary) { + return new MultiPartImpl(boundary); } - public static byte[] readContentToByte(MultipartStream multipartStream) throws IOException { - var output = new ByteArrayOutputStream(); - multipartStream.readBodyData(output); - return output.toByteArray(); + static MultiPartWritable of() { + return new MultiPartImpl("scx" + RandomUtils.randomString(10)); } - @Override - public boolean hasNext() { - return hasNextPart; - } - - @Override - public MultiPartPart next() { - if (!hasNextPart) { - throw new NoSuchElementException("No more parts available."); - } - try { - - // 读取当前部分的头部信息 - var headers = readToHeaders(multipartStream); - - //读取内容 - var content = readContentToByte(multipartStream); - - // 检查是否有下一个部分 - hasNextPart = multipartStream.readBoundary(); - - return new MultiPartPart(headers, content); - } catch (IOException e) { - throw new RuntimeException("Error reading next part", e); - } - } - - @Override - public Iterator iterator() { - return this; - } + String boundary(); } diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartImpl.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartImpl.java new file mode 100644 index 00000000..8dbe855b --- /dev/null +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartImpl.java @@ -0,0 +1,39 @@ +package cool.scx.http.media.multi_part; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class MultiPartImpl implements MultiPartWritable { + + private final List parts; + private String boundary; + + public MultiPartImpl(String boundary) { + this.boundary = boundary; + this.parts = new ArrayList<>(); + } + + @Override + public MultiPartWritable boundary(String boundary) { + this.boundary = boundary; + return this; + } + + @Override + public MultiPartWritable add(MultiPartPart part) { + parts.add(part); + return this; + } + + @Override + public String boundary() { + return boundary; + } + + @Override + public Iterator iterator() { + return parts.iterator(); + } + +} diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPart.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPart.java index 88b388f9..16dd64f9 100644 --- a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPart.java +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPart.java @@ -1,69 +1,70 @@ package cool.scx.http.media.multi_part; +import cool.scx.http.ScxHttpBody; import cool.scx.http.ScxHttpHeaders; import cool.scx.http.content_disposition.ContentDisposition; import cool.scx.http.content_type.ContentType; +import cool.scx.http.media.MediaReader; +import cool.scx.http.media.path.PathHelper; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.function.Supplier; -public class MultiPartPart { - - protected final ScxHttpHeaders headers; - protected final ContentDisposition contentDisposition; - protected final ContentType contentType; - protected final String name; - protected final String filename; - protected final String size; - protected final byte[] content; - protected final boolean isFile; - - public MultiPartPart(ScxHttpHeaders headers, byte[] content) { - this.headers = headers; - this.contentDisposition = headers.contentDisposition(); - this.contentType = headers.contentType(); - this.content = content; - if (this.contentDisposition != null) { - this.name = contentDisposition.name(); - this.filename = contentDisposition.filename(); - this.size = contentDisposition.size(); - this.isFile = filename != null; - } else { - this.name = null; - this.filename = null; - this.size = null; - this.isFile = false; - } +import static cool.scx.http.routing.handler.StaticHelper.getMediaTypeByFile; + +public interface MultiPartPart extends ScxHttpBody { + + static MultiPartPartWritable of() { + return new MultiPartPartImpl(); + } + + static MultiPartPartWritable of(String name, String value) { + return new MultiPartPartImpl().name(name).body(value); } - public ScxHttpHeaders headers() { - return headers; + static MultiPartPartWritable of(String name, Path value) { + var fileSize = PathHelper.getFileSize(value); + var contentType = getMediaTypeByFile(value); + var filename = value.getFileName().toString(); + return new MultiPartPartImpl().name(name).body(value).size(fileSize).filename(filename).contentType(contentType); } - public boolean isFile() { - return isFile; + ScxHttpHeaders headers(); + + Supplier body(); + + @Override + default InputStream inputStream() { + return body().get(); } - public String name() { - return name; + @Override + default T as(MediaReader t) { + return t.read(inputStream(), headers()); } - public String filename() { - return filename; + default ContentType contentType() { + return headers().contentType(); } - public String size() { - return size; + default ContentDisposition contentDisposition() { + return headers().contentDisposition(); } - public ContentType contentType() { - return contentType; + default String name() { + var contentDisposition = contentDisposition(); + return contentDisposition != null ? contentDisposition.name() : null; } - public ContentDisposition contentDisposition() { - return contentDisposition; + default String filename() { + var contentDisposition = contentDisposition(); + return contentDisposition != null ? contentDisposition.filename() : null; } - public byte[] content() { - return content; + default Long size() { + var contentDisposition = contentDisposition(); + return contentDisposition != null ? contentDisposition.size() : null; } } diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPartImpl.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPartImpl.java new file mode 100644 index 00000000..8535a7c0 --- /dev/null +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPartImpl.java @@ -0,0 +1,40 @@ +package cool.scx.http.media.multi_part; + +import cool.scx.http.ScxHttpHeaders; +import cool.scx.http.ScxHttpHeadersWritable; + +import java.io.InputStream; +import java.util.function.Supplier; + +public class MultiPartPartImpl implements MultiPartPartWritable { + + private ScxHttpHeadersWritable headers; + private Supplier body; + + public MultiPartPartImpl() { + this.headers = ScxHttpHeaders.of(); + } + + @Override + public MultiPartPartWritable headers(ScxHttpHeadersWritable headers) { + this.headers = headers; + return this; + } + + @Override + public MultiPartPartWritable body(Supplier os) { + body = os; + return this; + } + + @Override + public ScxHttpHeadersWritable headers() { + return headers; + } + + @Override + public Supplier body() { + return body; + } + +} diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPartWritable.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPartWritable.java new file mode 100644 index 00000000..2b694ecf --- /dev/null +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartPartWritable.java @@ -0,0 +1,88 @@ +package cool.scx.http.media.multi_part; + +import cool.scx.http.ScxHttpHeadersWritable; +import cool.scx.http.content_disposition.ContentDisposition; +import cool.scx.http.content_disposition.ContentDispositionWritable; +import cool.scx.http.content_type.ContentTypeWritable; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Supplier; + +public interface MultiPartPartWritable extends MultiPartPart { + + ScxHttpHeadersWritable headers(); + + MultiPartPartWritable headers(ScxHttpHeadersWritable headers); + + MultiPartPartWritable body(Supplier os); + + default MultiPartPartWritable contentType(ContentTypeWritable contentType) { + headers().contentType(contentType); + return this; + } + + default MultiPartPartWritable contentDisposition(ContentDispositionWritable contentDisposition) { + headers().contentDisposition(contentDisposition); + return this; + } + + default MultiPartPartWritable name(String name) { + var contentDisposition = headers().contentDisposition(); + if (contentDisposition != null) { + contentDisposition.name(name); + contentDisposition(contentDisposition); + } else { + contentDisposition(ContentDisposition.of().type("form-data").name(name)); + } + return this; + } + + default MultiPartPartWritable filename(String filename) { + var contentDisposition = headers().contentDisposition(); + if (contentDisposition != null) { + contentDisposition.filename(filename); + contentDisposition(contentDisposition); + } else { + contentDisposition(ContentDisposition.of().type("form-data").filename(filename)); + } + return this; + } + + default MultiPartPartWritable size(long size) { + var contentDisposition = headers().contentDisposition(); + if (contentDisposition != null) { + contentDisposition.size(size); + contentDisposition(contentDisposition); + } else { + contentDisposition(ContentDisposition.of().type("form-data").size(size)); + } + return this; + } + + default MultiPartPartWritable body(InputStream os) { + return body(() -> os); + } + + default MultiPartPartWritable body(byte[] os) { + return body(() -> new ByteArrayInputStream(os)); + } + + default MultiPartPartWritable body(String os) { + return body(() -> new ByteArrayInputStream(os.getBytes())); + } + + default MultiPartPartWritable body(Path os) { + return body(() -> { + try { + return Files.newInputStream(os); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + +} diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStream.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStream.java new file mode 100644 index 00000000..3834db1b --- /dev/null +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStream.java @@ -0,0 +1,81 @@ +package cool.scx.http.media.multi_part; + +import cool.scx.http.ScxHttpHeaders; +import cool.scx.http.ScxHttpHeadersWritable; +import org.apache.commons.fileupload.FileUploadBase; +import org.apache.commons.fileupload.MultipartStream; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class MultiPartStream implements MultiPart, Iterator { + + private final MultipartStream multipartStream; + private boolean hasNextPart; + private String boundary; + + public MultiPartStream(InputStream inputStream, String boundary) { + var boundaryBytes = boundary.getBytes(); + this.multipartStream = new MultipartStream(inputStream, boundaryBytes, 1024, null); + try { + hasNextPart = multipartStream.skipPreamble(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static ScxHttpHeadersWritable readToHeaders(MultipartStream multipartStream) throws MultipartStream.MalformedStreamException, FileUploadBase.FileUploadIOException { + var headersStr = multipartStream.readHeaders(); + return ScxHttpHeaders.of(headersStr); + } + + public static byte[] readContentToByte(MultipartStream multipartStream) throws IOException { + var output = new ByteArrayOutputStream(); + multipartStream.readBodyData(output); + return output.toByteArray(); + } + + @Override + public String boundary() { + return boundary; + } + + @Override + public Iterator iterator() { + return this; + } + + @Override + public boolean hasNext() { + return hasNextPart; + } + + @Override + public MultiPartPart next() { + if (!hasNextPart) { + throw new NoSuchElementException("No more parts available."); + } + try { + + // 读取当前部分的头部信息 + var headers = readToHeaders(multipartStream); + + var part = new MultiPartPartImpl().headers(headers); + + //读取内容 + var content = readContentToByte(multipartStream); + part.body(content); + + // 检查是否有下一个部分 + hasNextPart = multipartStream.readBoundary(); + + return part; + } catch (IOException e) { + throw new RuntimeException("Error reading next part", e); + } + } + +} diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPart.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamCached.java similarity index 71% rename from scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPart.java rename to scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamCached.java index d029d073..e96c4ef8 100644 --- a/scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPart.java +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamCached.java @@ -11,16 +11,17 @@ import java.util.Iterator; import java.util.NoSuchElementException; -import static cool.scx.http.media.multi_part.MultiPart.readContentToByte; -import static cool.scx.http.media.multi_part.MultiPart.readToHeaders; +import static cool.scx.http.media.multi_part.MultiPartStream.readContentToByte; +import static cool.scx.http.media.multi_part.MultiPartStream.readToHeaders; -public class CachedMultiPart implements Iterable, Iterator { +public class MultiPartStreamCached implements MultiPart, Iterator { private final MultipartStream multipartStream; private final Path cachePath; private boolean hasNextPart; + private String boundary; - public CachedMultiPart(InputStream inputStream, String boundary, Path cachePath) { + public MultiPartStreamCached(InputStream inputStream, String boundary, Path cachePath) { this.cachePath = cachePath; var boundaryBytes = boundary.getBytes(); this.multipartStream = new MultipartStream(inputStream, boundaryBytes, 1024, null); @@ -49,13 +50,23 @@ public static Path readContentToPath(MultipartStream multipartStream, Path path) return path; } + @Override + public String boundary() { + return boundary; + } + + @Override + public Iterator iterator() { + return this; + } + @Override public boolean hasNext() { return hasNextPart; } @Override - public CachedMultiPartPart next() { + public MultiPartPart next() { if (!hasNextPart) { throw new NoSuchElementException("No more parts available."); } @@ -64,27 +75,23 @@ public CachedMultiPartPart next() { // 读取当前部分的头部信息 var headers = readToHeaders(multipartStream); - byte[] content = null; - Path contentPath = null; + var part = new MultiPartPartImpl().headers(headers); var b = needCached(headers); if (b) { - contentPath = readContentToPath(multipartStream, cachePath.resolve(RandomUtils.randomString(32))); + var contentPath = readContentToPath(multipartStream, cachePath.resolve(RandomUtils.randomString(32))); + part.body(contentPath); } else { - content = readContentToByte(multipartStream); + var content = readContentToByte(multipartStream); + part.body(content); } // 检查是否有下一个部分 hasNextPart = multipartStream.readBoundary(); - return new CachedMultiPartPart(headers, content, contentPath); + return part; } catch (IOException e) { throw new RuntimeException("Error reading next part", e); } } - @Override - public Iterator iterator() { - return this; - } - } diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPartReader.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamCachedReader.java similarity index 68% rename from scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPartReader.java rename to scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamCachedReader.java index 3110230d..f1268de3 100644 --- a/scx-http/src/main/java/cool/scx/http/media/multi_part/CachedMultiPartReader.java +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamCachedReader.java @@ -7,22 +7,22 @@ import java.io.InputStream; import java.nio.file.Path; -public class CachedMultiPartReader implements MediaReader { +public class MultiPartStreamCachedReader implements MediaReader { - public static final CachedMultiPartReader CACHED_MULTI_PART_READER = new CachedMultiPartReader(); + public static final MultiPartStreamCachedReader MULTI_PART_READER_CACHED = new MultiPartStreamCachedReader(); private final Path cachePath; - public CachedMultiPartReader(Path cachePath) { + public MultiPartStreamCachedReader(Path cachePath) { this.cachePath = cachePath; } - public CachedMultiPartReader() { + public MultiPartStreamCachedReader() { this.cachePath = Path.of(System.getProperty("java.io.tmpdir")).resolve(".SCX-CACHE"); } @Override - public CachedMultiPart read(InputStream inputStream, ScxHttpHeaders headers) { + public MultiPart read(InputStream inputStream, ScxHttpHeaders headers) { var contentType = headers.contentType(); if (contentType == null) { throw new IllegalArgumentException("No Content-Type header found"); @@ -34,7 +34,7 @@ public CachedMultiPart read(InputStream inputStream, ScxHttpHeaders headers) { if (boundary == null) { throw new IllegalArgumentException("No boundary found"); } - return new CachedMultiPart(inputStream, boundary, cachePath); + return new MultiPartStreamCached(inputStream, boundary, cachePath); } } diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartReader.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamReader.java similarity index 77% rename from scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartReader.java rename to scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamReader.java index a60f6a20..590839f1 100644 --- a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartReader.java +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartStreamReader.java @@ -6,9 +6,9 @@ import java.io.InputStream; -public class MultiPartReader implements MediaReader { +public class MultiPartStreamReader implements MediaReader { - public static final MultiPartReader MULTI_PART_READER = new MultiPartReader(); + public static final MultiPartStreamReader MULTI_PART_READER = new MultiPartStreamReader(); @Override public MultiPart read(InputStream inputStream, ScxHttpHeaders headers) { @@ -23,7 +23,7 @@ public MultiPart read(InputStream inputStream, ScxHttpHeaders headers) { if (boundary == null) { throw new IllegalArgumentException("No boundary found"); } - return new MultiPart(inputStream, boundary); + return new MultiPartStream(inputStream, boundary); } } diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartWritable.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartWritable.java new file mode 100644 index 00000000..742c69eb --- /dev/null +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartWritable.java @@ -0,0 +1,19 @@ +package cool.scx.http.media.multi_part; + +import java.nio.file.Path; + +public interface MultiPartWritable extends MultiPart { + + MultiPartWritable boundary(String boundary); + + MultiPartWritable add(MultiPartPart part); + + default MultiPartWritable add(String name, String value) { + return add(MultiPartPart.of(name, value)); + } + + default MultiPartWritable add(String name, Path value) { + return add(MultiPartPart.of(name, value)); + } + +} diff --git a/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartWriter.java b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartWriter.java new file mode 100644 index 00000000..9521575e --- /dev/null +++ b/scx-http/src/main/java/cool/scx/http/media/multi_part/MultiPartWriter.java @@ -0,0 +1,59 @@ +package cool.scx.http.media.multi_part; + +import cool.scx.http.ScxHttpHeaders; +import cool.scx.http.ScxHttpHeadersWritable; +import cool.scx.http.content_type.ContentType; +import cool.scx.http.media.MediaWriter; + +import java.io.OutputStream; + +import static cool.scx.http.MediaType.MULTIPART_FORM_DATA; + +public class MultiPartWriter implements MediaWriter { + + private final MultiPart multiPart; + + public MultiPartWriter(MultiPart multiPart) { + this.multiPart = multiPart; + } + + @Override + public void beforeWrite(ScxHttpHeadersWritable headersWritable, ScxHttpHeaders headers) { + if (headersWritable.contentType() == null) { + // MULTIPART 有很多类型 这里暂时只当成 MULTIPART_FORM_DATA + headersWritable.contentType(ContentType.of().mediaType(MULTIPART_FORM_DATA).boundary(this.multiPart.boundary())); + } + } + + @Override + public void write(OutputStream outputStream) { + //头 + var h = ("--" + multiPart.boundary() + "\r\n").getBytes(); + //尾 + var f = ("--" + multiPart.boundary() + "--\r\n").getBytes(); + //换行符 + var l = "\r\n".getBytes(); + try (outputStream) { + //发送每个内容 + for (var multiPartPart : multiPart) { + //发送头 + outputStream.write(h); + var headers = multiPartPart.headers().encode(); + //写入头 + outputStream.write(headers.getBytes()); + //写入换行符 + outputStream.write(l); + //写入内容 + try (var i = multiPartPart.inputStream()) { + i.transferTo(outputStream); + } + //写入换行符 + outputStream.write(l); + } + outputStream.write(f); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/scx-http/src/main/java/cool/scx/http/media/path/PathReader.java b/scx-http/src/main/java/cool/scx/http/media/path/PathReader.java index 1eb56073..70d25937 100644 --- a/scx-http/src/main/java/cool/scx/http/media/path/PathReader.java +++ b/scx-http/src/main/java/cool/scx/http/media/path/PathReader.java @@ -29,8 +29,7 @@ public PathReader(Path path, OpenOption... options) { @Override public Path read(InputStream inputStream, ScxHttpHeaders headers) { - try { - var outputStream = Files.newOutputStream(path, options); + try (var outputStream = Files.newOutputStream(path, options)) { inputStream.transferTo(outputStream); } catch (IOException e) { throw new RuntimeException(e); diff --git a/scx-http/src/main/java/cool/scx/http/routing/handler/StaticHelper.java b/scx-http/src/main/java/cool/scx/http/routing/handler/StaticHelper.java index 1bd3fd93..235736a3 100644 --- a/scx-http/src/main/java/cool/scx/http/routing/handler/StaticHelper.java +++ b/scx-http/src/main/java/cool/scx/http/routing/handler/StaticHelper.java @@ -2,6 +2,7 @@ import cool.scx.http.FileFormat; import cool.scx.http.content_type.ContentType; +import cool.scx.http.content_type.ContentTypeWritable; import cool.scx.http.exception.NotFoundException; import cool.scx.http.media.path.PathHelper; import cool.scx.http.range.Range; @@ -76,7 +77,7 @@ public static void sendStatic(Path path, RoutingContext context) { } - public static ContentType getMediaTypeByFile(Path path) { + public static ContentTypeWritable getMediaTypeByFile(Path path) { var fileFormat = FileFormat.ofFileName(path.getFileName().toString()); if (fileFormat == null) { fileFormat = FileFormat.BIN; diff --git a/scx-web/src/main/java/cool/scx/web/parameter_handler/FileUploadParameterHandler.java b/scx-web/src/main/java/cool/scx/web/parameter_handler/FileUploadParameterHandler.java index 844f3293..c9d6cdbc 100644 --- a/scx-web/src/main/java/cool/scx/web/parameter_handler/FileUploadParameterHandler.java +++ b/scx-web/src/main/java/cool/scx/web/parameter_handler/FileUploadParameterHandler.java @@ -1,7 +1,6 @@ package cool.scx.web.parameter_handler; import cool.scx.common.util.AnnotationUtils; -import cool.scx.http.media.multi_part.CachedMultiPartPart; import cool.scx.http.media.multi_part.MultiPartPart; import cool.scx.reflect.ParameterInfo; import cool.scx.web.annotation.FromUpload; @@ -36,7 +35,7 @@ private static MultiPartPart[] findFileUploadListByName(RequestInfo routingConte public boolean canHandle(ParameterInfo parameter) { var isArray = parameter.type().isCollectionLikeType() || parameter.type().isArrayType(); var rawType = isArray ? parameter.type().getContentType().getRawClass() : parameter.type().getRawClass(); - return rawType == MultiPartPart.class || rawType == CachedMultiPartPart.class; + return rawType == MultiPartPart.class; } @Override diff --git a/scx-web/src/main/java/cool/scx/web/parameter_handler/RequestInfo.java b/scx-web/src/main/java/cool/scx/web/parameter_handler/RequestInfo.java index 6640469d..3b8f3ce5 100644 --- a/scx-web/src/main/java/cool/scx/web/parameter_handler/RequestInfo.java +++ b/scx-web/src/main/java/cool/scx/web/parameter_handler/RequestInfo.java @@ -98,12 +98,13 @@ private void initBody(RoutingContext ctx, ContentType contentType) { var m = new MultiMap(); var f = new MultiMap(); //文件和非文件 - var multiPart = ctx.request().body().asCachedMultiPart(); + var multiPart = ctx.request().body().asMultiPartCached(); for (var multiPartPart : multiPart) { - if (multiPartPart.isFile()) { + //没有文件名我们就当成 空文件 + if (multiPartPart.filename() != null) { f.put(multiPartPart.name(), multiPartPart); } else { - m.put(multiPartPart.name(), new String(multiPartPart.content())); + m.put(multiPartPart.name(), multiPartPart.asString()); } } this.body = jsonMapper().convertValue(m.toMultiValueMap(), JsonNode.class); diff --git a/scx-web/src/main/java/cool/scx/web/vo/Download.java b/scx-web/src/main/java/cool/scx/web/vo/Download.java index f29dc99e..2b62aed4 100644 --- a/scx-web/src/main/java/cool/scx/web/vo/Download.java +++ b/scx-web/src/main/java/cool/scx/web/vo/Download.java @@ -4,7 +4,7 @@ import java.nio.file.Path; import static cool.scx.http.HttpHelper.getDownloadContentDisposition; -import static cool.scx.http.HttpHelper.getMediaTypeFromFileName; +import static cool.scx.http.HttpHelper.getMediaTypeByFileName; /** * 文件下载 vo @@ -15,15 +15,15 @@ public final class Download extends BaseWriter { private Download(InputStream inputStream, String downloadName) { - super(inputStream, getMediaTypeFromFileName(downloadName), getDownloadContentDisposition(downloadName)); + super(inputStream, getMediaTypeByFileName(downloadName), getDownloadContentDisposition(downloadName)); } private Download(Path path, String downloadName) { - super(path, getMediaTypeFromFileName(downloadName), getDownloadContentDisposition(downloadName)); + super(path, getMediaTypeByFileName(downloadName), getDownloadContentDisposition(downloadName)); } private Download(byte[] bytes, String downloadName) { - super(bytes, getMediaTypeFromFileName(downloadName), getDownloadContentDisposition(downloadName)); + super(bytes, getMediaTypeByFileName(downloadName), getDownloadContentDisposition(downloadName)); } public static Download of(InputStream inputStream, String downloadName) {