diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartBinaryWithoutFilenameTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartBinaryWithoutFilenameTest.java new file mode 100644 index 0000000000000..ef0a01bb95f71 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartBinaryWithoutFilenameTest.java @@ -0,0 +1,67 @@ +package io.quarkus.resteasy.reactive.server.test.multipart; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.function.Supplier; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.MediaType; + +import org.jboss.resteasy.reactive.PartType; +import org.jboss.resteasy.reactive.RestForm; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class MultipartBinaryWithoutFilenameTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(MultipartDataInputTest.Resource.class, MultipartDataInputTest.Item.class, + MultipartDataInputTest.Result.class); + } + }); + private final File IMAGE_FILE = new File("./src/test/resources/image.png"); + + @Test + public void test() throws IOException { + byte[] bytes = given() + .contentType("multipart/form-data") + .multiPart("bytes", IMAGE_FILE, "application/png") + .when() + .post("/test") + .then() + .statusCode(200) + .extract().body().asByteArray(); + + assertThat(bytes).isEqualTo(Files.readAllBytes(IMAGE_FILE.toPath())); + } + + @Path("/test") + public static class Resource { + + @POST + public byte[] testMultipart(Input input) { + return input.bytes; + } + } + + public static class Input { + @RestForm("bytes") + @PartType(MediaType.APPLICATION_OCTET_STREAM) + public byte[] bytes; + + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/resources/image.png b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/resources/image.png new file mode 100644 index 0000000000000..5a0e1e7e186e3 Binary files /dev/null and b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/resources/image.png differ diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultiPartParserDefinition.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultiPartParserDefinition.java index b368678792aa8..42ed5899e7356 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultiPartParserDefinition.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultiPartParserDefinition.java @@ -23,6 +23,7 @@ import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.container.CompletionCallback; import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.jboss.logging.Logger; @@ -339,24 +340,35 @@ public void endPart() { contentBytes.reset(); } else { - try { - String charset = defaultEncoding; - String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); - if (contentType != null) { - String cs = HeaderUtil.extractQuotedValueFromHeader(contentType, "charset"); + String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); + if (isText(contentType)) { + try { + String charset = defaultEncoding; + String cs = contentType != null ? HeaderUtil.extractQuotedValueFromHeader(contentType, "charset") + : null; if (cs != null) { charset = cs; } - } - data.add(currentName, contentBytes.toString(charset), charset, headers); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); + data.add(currentName, contentBytes.toString(charset), charset, headers); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } else { + data.add(currentName, Arrays.copyOf(contentBytes.toByteArray(), contentBytes.size()), null, headers); } + contentBytes.reset(); } } + private boolean isText(String contentType) { + if (contentType == null || contentType.isEmpty()) { // https://www.rfc-editor.org/rfc/rfc7578.html#section-4.4 says the default content-type if missing is text/plain + return true; + } + return MediaType.TEXT_PLAIN_TYPE.isCompatible(MediaType.valueOf(contentType)); + } + public List getCreatedFiles() { return createdFiles; } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartSupport.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartSupport.java index b6dddabfe2667..6c048c672b48e 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartSupport.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartSupport.java @@ -30,6 +30,7 @@ import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.core.ServerSerialisers; import org.jboss.resteasy.reactive.server.handlers.RequestDeserializeHandler; +import org.jboss.resteasy.reactive.server.multipart.FileItem; import org.jboss.resteasy.reactive.server.multipart.FormValue; import org.jboss.resteasy.reactive.server.multipart.MultipartFormDataInput; import org.jboss.resteasy.reactive.server.multipart.MultipartPartReadingException; @@ -90,7 +91,7 @@ private static Object read(MessageBodyReader reader, String attributeName, Fo @Override public InputStream get() { try { - return Files.newInputStream(value.getFileItem().getFile()); + return value.getFileItem().getInputStream(); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -248,7 +249,11 @@ public static byte[] getByteArray(String formName, ResteasyReactiveRequestContex } if (value.isFileItem()) { try { - return Files.readAllBytes(value.getFileItem().getFile()); + FileItem fileItem = value.getFileItem(); + if (fileItem.isInMemory()) { + return fileItem.getInputStream().readAllBytes(); + } + return Files.readAllBytes(fileItem.getFile()); } catch (IOException e) { throw new MultipartPartReadingException(e); }