Skip to content

Commit

Permalink
Merge pull request quarkusio#40094 from geoand/quarkusio#40052
Browse files Browse the repository at this point in the history
Support `FileUpload` as multipart type in REST Client
  • Loading branch information
geoand authored Apr 16, 2024
2 parents cb01f5a + b1cc21b commit 8612234
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 5 deletions.
8 changes: 4 additions & 4 deletions docs/src/main/asciidoc/rest-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ To send data as a multipart form, you can just use the regular `@RestForm` (or `
String sendMultipart(@RestForm File file, @RestForm String otherField);
----

Parameters specified as `File`, `Path`, `byte[]` or `Buffer` are sent as files and default to the
Parameters specified as `File`, `Path`, `byte[]`, `Buffer` or `FileUpload` are sent as files and default to the
`application/octet-stream` MIME type. Other `@RestForm` parameter types default to the `text/plain`
MIME type. You can override these defaults with the `@PartType` annotation.

Expand All @@ -1421,7 +1421,7 @@ Naturally, you can also group these parameters into a containing class:
String sendMultipart(Parameters parameters);
----

Any `@RestForm` parameter of the type `File`, `Path`, `byte[]` or `Buffer`, as well as any
Any `@RestForm` parameter of the type `File`, `Path`, `byte[]`, `Buffer` or `FileUpload`, as well as any
annotated with `@PartType` automatically imply a `@Consumes(MediaType.MULTIPART_FORM_DATA)`
on the method if there is no `@Consumes` present.

Expand Down Expand Up @@ -1533,15 +1533,15 @@ public ClientMultipartForm buildClientMultipartForm(Request request) { // <1>
ClientMultipartForm multiPartForm = ClientMultipartForm.create();
multiPartForm.attribute("jsonPayload", request.getJsonPayload(), "jsonPayload"); // <2>
request.getFiles().forEach(fu -> {
multiPartForm.binaryFileUpload("file", fu.name(), fu.filePath().toString(), fu.contentType()); // <3>
multiPartForm.fileUpload(fu); // <3>
});
return multiPartForm;
}
----

<1> `Request` representing the request the server parts accepts
<2> A `jsonPayload` attribute is added directly to `ClientMultipartForm`
<3> A `binaryFileUpload` is created from the request's `FileUpload` (which is a Quarkus REST (Server) type used to represent a binary file upload)
<3> A `fileUpload` is created from the request's `FileUpload`

[NOTE]
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames;
import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult;
import org.jboss.resteasy.reactive.multipart.FileDownload;
import org.jboss.resteasy.reactive.multipart.FileUpload;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
Expand Down Expand Up @@ -190,6 +191,7 @@ public class JaxrsClientReactiveProcessor {
private static final String PATH_SIGNATURE = "L" + java.nio.file.Path.class.getName().replace('.', '/') + ";";
private static final String BUFFER_SIGNATURE = "L" + Buffer.class.getName().replace('.', '/') + ";";
private static final String BYTE_ARRAY_SIGNATURE = "[B";
private static final String FILE_UPLOAD_SIGNATURE = "L" + FileUpload.class.getName().replace('.', '/') + ";";

private static final Logger log = Logger.getLogger(JaxrsClientReactiveProcessor.class);

Expand Down Expand Up @@ -1176,6 +1178,7 @@ private boolean isMultipartRequiringType(String signature, String partType) {
|| signature.equals(BUFFER_SIGNATURE)
|| signature.equals(BYTE_ARRAY_SIGNATURE)
|| signature.equals(MULTI_BYTE_SIGNATURE)
|| signature.equals(FILE_UPLOAD_SIGNATURE)
|| partType != null);
}

Expand Down Expand Up @@ -1793,6 +1796,8 @@ private void handleMultipartField(String formParamName, String partType, String
} else if (type.equals(Path.class.getName())) {
// and so is path
addFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue);
} else if (type.equals(FileUpload.class.getName())) {
addFileUpload(fieldValue, multipartForm, methodCreator);
} else if (type.equals(InputStream.class.getName())) {
// and so is path
addInputStream(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, type);
Expand Down Expand Up @@ -1888,6 +1893,15 @@ private void addFile(BytecodeCreator methodCreator, AssignableResultHandle multi
}
}

private void addFileUpload(ResultHandle fieldValue, AssignableResultHandle multipartForm,
BytecodeCreator methodCreator) {
// MultipartForm#fileUpload(FileUpload fileUpload);
methodCreator.invokeVirtualMethod(
MethodDescriptor.ofMethod(ClientMultipartForm.class, "fileUpload",
ClientMultipartForm.class, FileUpload.class),
multipartForm, fieldValue);
}

private ResultHandle primitiveToString(BytecodeCreator methodCreator, ResultHandle fieldValue, FieldInfo field) {
PrimitiveType primitiveType = field.type().asPrimitiveType();
switch (primitiveType.primitive()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,39 @@ void shouldCallImplicitEndpoints() throws IOException {
.isEqualTo(file.getName() + " file Hello");
assertThat(client.postMultipartEntityImplicit(file.getName(), person))
.isEqualTo(file.getName() + " Stef:Epardaud");

assertThat(client.postMultipartImplicitFileUpload("Foo", new FileUpload() {
@Override
public String name() {
return "file";
}

@Override
public java.nio.file.Path filePath() {
return file.toPath();
}

@Override
public String fileName() {
return file.getName();
}

@Override
public long size() {
return -1;
}

@Override
public String contentType() {
return "application/octet-stream";
}

@Override
public String charSet() {
return "";
}
}))
.isEqualTo("Foo " + file.getName() + " Hello");
}

@Path("form")
Expand Down Expand Up @@ -142,6 +175,10 @@ String postMultipartEntityImplicit(@RestForm String name,
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartExplicit(@RestForm String name, @RestForm File file);

@Path("multipart")
@POST
String postMultipartImplicitFileUpload(@RestForm String name, @RestForm FileUpload file);

@Path("urlencoded")
@POST
String postUrlencodedImplicit(@RestForm String name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,49 @@ void shouldPassOriginalFileName() throws IOException {
assertThat(client.postMultipart(form)).isEqualTo(file.getName());
}

@Test
void shouldWorkWithFileUpload() throws IOException {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);

File file = File.createTempFile("MultipartTest", ".txt");
file.deleteOnExit();

ClientFormUsingFileUpload form = new ClientFormUsingFileUpload();
form.file = new FileUpload() {

@Override
public String name() {
return "myFile";
}

@Override
public java.nio.file.Path filePath() {
return file.toPath();
}

@Override
public String fileName() {
return file.getName();
}

@Override
public long size() {
return 0;
}

@Override
public String contentType() {
return "application/octet-stream";
}

@Override
public String charSet() {
return "";
}
};
assertThat(client.postMultipartFileUpload(form)).isEqualTo(file.getName());
}

@Test
void shouldUseFileNameFromAnnotation() throws IOException {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
Expand Down Expand Up @@ -244,6 +287,10 @@ public interface Client {
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipart(@MultipartForm ClientForm clientForm);

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartFileUpload(ClientFormUsingFileUpload clientForm);

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartWithPartFilename(@MultipartForm ClientFormUsingFile clientForm);
Expand Down Expand Up @@ -324,6 +371,11 @@ public static class ClientForm {
public File file;
}

public static class ClientFormUsingFileUpload {
@RestForm
public FileUpload file;
}

public static class ClientFormUsingFile {
@FormParam("myFile")
@PartType(APPLICATION_OCTET_STREAM)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartForm;
import org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartFormDataPart;
import org.jboss.resteasy.reactive.multipart.FileUpload;

import io.smallrye.mutiny.Multi;
import io.vertx.core.buffer.Buffer;
Expand Down Expand Up @@ -86,4 +87,9 @@ public ClientMultipartForm multiAsTextFileUpload(String name, String filename, M
return this;
}

public ClientMultipartForm fileUpload(FileUpload fileUpload) {
binaryFileUpload(fileUpload.name(), fileUpload.fileName(), fileUpload.filePath().toString(), fileUpload.contentType());
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* Represent a file that has been uploaded.
* <p>
* WARNING: This type is currently only supported on the server
* This type is usually used on server, but it is also supported in the REST Client.
*/
public interface FileUpload extends FilePart {

Expand Down

0 comments on commit 8612234

Please sign in to comment.