diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index 6444298af3a19..6441b9a47904f 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -1142,6 +1142,7 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand String formParamName = formParamName(field); String partType = formPartType(field); + String partFilename = formPartFilename(field); Type fieldType = field.type(); @@ -1152,7 +1153,7 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand // we support string, and send it as an attribute ClassInfo fieldClass = index.getClassByName(fieldType.name()); if (DotNames.STRING.equals(fieldClass.name())) { - addString(ifValueNotNull, multipartForm, formParamName, fieldValue); + addString(ifValueNotNull, multipartForm, formParamName, partFilename, fieldValue); } else if (is(FILE, fieldClass, index)) { // file is sent as file :) if (partType == null) { @@ -1162,7 +1163,7 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand } ResultHandle filePath = ifValueNotNull.invokeVirtualMethod( MethodDescriptor.ofMethod(File.class, "toPath", Path.class), fieldValue); - addFile(ifValueNotNull, multipartForm, formParamName, partType, filePath); + addFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, filePath); } else if (is(PATH, fieldClass, index)) { // and so is path if (partType == null) { @@ -1170,10 +1171,10 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand "No @PartType annotation found on multipart form field of type Path: " + formClass.name() + "." + field.name()); } - addFile(ifValueNotNull, multipartForm, formParamName, partType, fieldValue); + addFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue); } else if (is(BUFFER, fieldClass, index)) { // and buffer - addBuffer(ifValueNotNull, multipartForm, formParamName, partType, fieldValue, field); + addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, field); } else { // assume POJO: addPojo(ifValueNotNull, multipartForm, formParamName, partType, fieldValue, field); } @@ -1189,12 +1190,12 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand ResultHandle buffer = ifValueNotNull.invokeStaticInterfaceMethod( MethodDescriptor.ofMethod(Buffer.class, "buffer", Buffer.class, byte[].class), fieldValue); - addBuffer(ifValueNotNull, multipartForm, formParamName, partType, buffer, field); + addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, buffer, field); break; case PRIMITIVE: // primitives are converted to text and sent as attribute ResultHandle string = primitiveToString(ifValueNotNull, fieldValue, field); - addString(ifValueNotNull, multipartForm, formParamName, string); + addString(ifValueNotNull, multipartForm, formParamName, partFilename, string); break; case PARAMETERIZED_TYPE: ParameterizedType parameterizedType = fieldType.asParameterizedType(); @@ -1232,9 +1233,10 @@ private void addPojo(BytecodeCreator methodCreator, AssignableResultHandle multi * {@link QuarkusMultipartForm#textFileUpload(String, String, String, String)} */ private void addFile(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName, - String partType, ResultHandle filePath) { + String partType, String partFilename, ResultHandle filePath) { ResultHandle fileNamePath = methodCreator.invokeInterfaceMethod(PATH_GET_FILENAME, filePath); - ResultHandle fileName = methodCreator.invokeVirtualMethod(OBJECT_TO_STRING, fileNamePath); + ResultHandle fileName = partFilename != null ? methodCreator.load(partFilename) + : methodCreator.invokeVirtualMethod(OBJECT_TO_STRING, fileNamePath); ResultHandle pathString = methodCreator.invokeVirtualMethod(OBJECT_TO_STRING, filePath); if (partType.equalsIgnoreCase(MediaType.APPLICATION_OCTET_STREAM)) { methodCreator.assign(multipartForm, @@ -1286,13 +1288,18 @@ private ResultHandle primitiveToString(BytecodeCreator methodCreator, ResultHand } } + private ResultHandle partFilenameHandle(BytecodeCreator methodCreator, String partFilename) { + return partFilename != null ? methodCreator.load(partFilename) : methodCreator.loadNull(); + } + private void addString(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName, - ResultHandle fieldValue) { + String partFilename, ResultHandle fieldValue) { methodCreator.assign(multipartForm, methodCreator.invokeVirtualMethod( MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "attribute", QuarkusMultipartForm.class, - String.class, String.class), - multipartForm, methodCreator.load(formParamName), fieldValue)); + String.class, String.class, String.class), + multipartForm, methodCreator.load(formParamName), fieldValue, + partFilenameHandle(methodCreator, partFilename))); } private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName, @@ -1327,7 +1334,9 @@ private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandl } private void addBuffer(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName, - String partType, ResultHandle buffer, FieldInfo field) { + String partType, String partFilename, ResultHandle buffer, FieldInfo field) { + ResultHandle filenameHandle = partFilename != null ? methodCreator.load(partFilename) + : methodCreator.load(formParamName); if (partType == null) { throw new IllegalArgumentException( "No @PartType annotation found on multipart form field " + @@ -1336,22 +1345,20 @@ private void addBuffer(BytecodeCreator methodCreator, AssignableResultHandle mul if (partType.equalsIgnoreCase(MediaType.APPLICATION_OCTET_STREAM)) { methodCreator.assign(multipartForm, // MultipartForm#binaryFileUpload(String name, String filename, io.vertx.mutiny.core.buffer.Buffer content, String mediaType); - // filename = name methodCreator.invokeVirtualMethod( MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "binaryFileUpload", QuarkusMultipartForm.class, String.class, String.class, Buffer.class, String.class), - multipartForm, methodCreator.load(formParamName), methodCreator.load(formParamName), + multipartForm, methodCreator.load(formParamName), filenameHandle, buffer, methodCreator.load(partType))); } else { methodCreator.assign(multipartForm, // MultipartForm#textFileUpload(String name, String filename, io.vertx.mutiny.core.buffer.Buffer content, String mediaType) - // filename = name methodCreator.invokeVirtualMethod( MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "textFileUpload", QuarkusMultipartForm.class, String.class, String.class, Buffer.class, String.class), - multipartForm, methodCreator.load(formParamName), methodCreator.load(formParamName), + multipartForm, methodCreator.load(formParamName), filenameHandle, buffer, methodCreator.load(partType))); } } @@ -1364,6 +1371,14 @@ private String formPartType(FieldInfo field) { return null; } + private String formPartFilename(FieldInfo field) { + AnnotationInstance partType = field.annotation(ResteasyReactiveDotNames.PART_FILE_NAME); + if (partType != null) { + return partType.value().asString(); + } + return null; + } + private String formParamName(FieldInfo field) { AnnotationInstance restFormParam = field.annotation(ResteasyReactiveDotNames.REST_FORM_PARAM); AnnotationInstance formParam = field.annotation(ResteasyReactiveDotNames.FORM_PARAM); diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java index c77b541c0d366..333226f0a9bf4 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartFilenameTest.java @@ -15,6 +15,7 @@ import org.eclipse.microprofile.rest.client.RestClientBuilder; import org.jboss.resteasy.reactive.MultipartForm; +import org.jboss.resteasy.reactive.PartFilename; import org.jboss.resteasy.reactive.PartType; import org.jboss.resteasy.reactive.multipart.FileUpload; import org.junit.jupiter.api.Test; @@ -44,6 +45,18 @@ void shouldPassOriginalFileName() throws IOException { assertThat(client.postMultipart(form)).isEqualTo(file.getName()); } + @Test + void shouldUseFileNameFromAnnotation() throws IOException { + Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class); + + File file = File.createTempFile("MultipartTest", ".txt"); + file.deleteOnExit(); + + ClientForm2 form = new ClientForm2(); + form.file = file; + assertThat(client.postMultipartWithPartFilename(form)).isEqualTo(ClientForm2.FILE_NAME); + } + @Path("/multipart") @ApplicationScoped public static class Resource { @@ -66,6 +79,9 @@ public interface Client { @Consumes(MediaType.MULTIPART_FORM_DATA) String postMultipart(@MultipartForm ClientForm clientForm); + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + String postMultipartWithPartFilename(@MultipartForm ClientForm2 clientForm); } public static class ClientForm { @@ -73,4 +89,13 @@ public static class ClientForm { @PartType(MediaType.APPLICATION_OCTET_STREAM) public File file; } + + public static class ClientForm2 { + public static final String FILE_NAME = "clientFile"; + + @FormParam("myFile") + @PartType(MediaType.APPLICATION_OCTET_STREAM) + @PartFilename(FILE_NAME) + public File file; + } } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java index 12ee1d958c0c1..54c244fe2adc3 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartForm.java @@ -41,8 +41,8 @@ public Charset getCharset() { return charset; } - public QuarkusMultipartForm attribute(String name, String value) { - parts.add(new QuarkusMultipartFormDataPart(name, value)); + public QuarkusMultipartForm attribute(String name, String value, String filename) { + parts.add(new QuarkusMultipartFormDataPart(name, value, filename)); return this; } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java index 218e3d38222e0..c5f8b9e70e338 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormDataPart.java @@ -63,7 +63,7 @@ public QuarkusMultipartFormDataPart(String name, String filename, Multi co this.content = null; } - public QuarkusMultipartFormDataPart(String name, String value) { + public QuarkusMultipartFormDataPart(String name, String value, String filename) { if (name == null) { throw new NullPointerException("Multipart field name cannot be null"); } @@ -72,7 +72,7 @@ public QuarkusMultipartFormDataPart(String name, String value) { } this.name = name; this.value = value; - this.filename = null; + this.filename = filename; this.pathname = null; this.content = null; this.multiByteContent = null; diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java index d963fdbeb5a0c..612d273e2cded 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java @@ -81,6 +81,7 @@ import org.jboss.jandex.DotName; import org.jboss.resteasy.reactive.DummyElementType; import org.jboss.resteasy.reactive.MultipartForm; +import org.jboss.resteasy.reactive.PartFilename; import org.jboss.resteasy.reactive.PartType; import org.jboss.resteasy.reactive.RestCookie; import org.jboss.resteasy.reactive.RestForm; @@ -126,6 +127,7 @@ public final class ResteasyReactiveDotNames { public static final DotName REST_FORM_PARAM = DotName.createSimple(RestForm.class.getName()); public static final DotName MULTI_PART_FORM_PARAM = DotName.createSimple(MultipartForm.class.getName()); public static final DotName PART_TYPE_NAME = DotName.createSimple(PartType.class.getName()); + public static final DotName PART_FILE_NAME = DotName.createSimple(PartFilename.class.getName()); public static final DotName REST_MATRIX_PARAM = DotName.createSimple(RestMatrix.class.getName()); public static final DotName REST_COOKIE_PARAM = DotName.createSimple(RestCookie.class.getName()); public static final DotName GET = DotName.createSimple(javax.ws.rs.GET.class.getName()); diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/PartFilename.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/PartFilename.java new file mode 100644 index 0000000000000..1e5bff737da80 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/PartFilename.java @@ -0,0 +1,16 @@ +package org.jboss.resteasy.reactive; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Used on fields of {@link MultipartForm} POJOs to designate the filename of a part. + * This is only applicable in the client, not the server. + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface PartFilename { + String value(); +}