Skip to content

Commit

Permalink
Support @PartFilename on InputStream and Multi<Byte> fields
Browse files Browse the repository at this point in the history
This pull request fixes the support of the annotation @PartFilename on InputStream and Multi<Byte> types.
Also, it extends the coverage we had to also verify these types and byte[].

Fix quarkusio#32032
  • Loading branch information
Sgitario committed Mar 23, 2023
1 parent 7ab6009 commit 4717c90
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.io.Closeable;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.nio.file.Path;
import java.util.AbstractMap;
Expand Down Expand Up @@ -1649,6 +1650,9 @@ 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(InputStream.class.getName())) {
// and so is path
addInputStream(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, type);
} else if (type.equals(Buffer.class.getName())) {
// and buffer
addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, errorLocation);
Expand All @@ -1663,8 +1667,12 @@ private void handleMultipartField(String formParamName, String partType, String
fieldValue);
addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, buffer, errorLocation);
} else if (parameterGenericType.equals(MULTI_BYTE_SIGNATURE)) {
addMultiAsFile(ifValueNotNull, multipartForm, formParamName, partType, fieldValue, errorLocation);
addMultiAsFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, errorLocation);
} else if (partType != null) {
if (partFilename != null) {
log.warnf("Using the @PartFilename annotation is unsupported on the type '%s'. Problematic field is: '%s'",
partType, formParamName);
}
// assume POJO:
addPojo(ifValueNotNull, multipartForm, formParamName, partType, fieldValue, type);
} else {
Expand All @@ -1677,6 +1685,17 @@ private void handleMultipartField(String formParamName, String partType, String
}
}

private void addInputStream(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
String partType, String partFilename, ResultHandle fieldValue, String type) {
methodCreator.assign(multipartForm,
methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "entity",
QuarkusMultipartForm.class, String.class, String.class, Object.class, String.class, Class.class),
multipartForm, methodCreator.load(formParamName), methodCreator.load(partFilename), fieldValue,
methodCreator.load(partType),
// FIXME: doesn't support generics
methodCreator.loadClassFromTCCL(type)));
}

private void addPojo(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
String partType, ResultHandle fieldValue, String type) {
methodCreator.assign(multipartForm,
Expand Down Expand Up @@ -1780,12 +1799,17 @@ private void addString(BytecodeCreator methodCreator, AssignableResultHandle mul
}

private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
String partType,
String partType, String partFilename,
ResultHandle multi, String errorLocation) {
// they all default to plain/text except buffers/byte[]/Multi<Byte>/File/Path
if (partType == null) {
partType = MediaType.APPLICATION_OCTET_STREAM;
}
String filename = partFilename;
if (filename == null) {
filename = formParamName;
}

if (partType.equalsIgnoreCase(MediaType.APPLICATION_OCTET_STREAM)) {
methodCreator.assign(multipartForm,
// MultipartForm#binaryFileUpload(String name, String filename, Multi<Byte> content, String mediaType);
Expand All @@ -1794,7 +1818,7 @@ private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandl
MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "multiAsBinaryFileUpload",
QuarkusMultipartForm.class, String.class, String.class, Multi.class,
String.class),
multipartForm, methodCreator.load(formParamName), methodCreator.load(formParamName),
multipartForm, methodCreator.load(formParamName), methodCreator.load(filename),
multi, methodCreator.load(partType)));
} else {
methodCreator.assign(multipartForm,
Expand All @@ -1804,7 +1828,7 @@ private void addMultiAsFile(BytecodeCreator methodCreator, AssignableResultHandl
MethodDescriptor.ofMethod(QuarkusMultipartForm.class, "multiAsTextFileUpload",
QuarkusMultipartForm.class, String.class, String.class, Multi.class,
String.class),
multipartForm, methodCreator.load(formParamName), methodCreator.load(formParamName),
multipartForm, methodCreator.load(formParamName), methodCreator.load(filename),
multi, methodCreator.load(partType)));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import static org.assertj.core.api.Assertions.assertThat;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.stream.Collectors;

import jakarta.enterprise.context.ApplicationScoped;
Expand All @@ -29,6 +31,7 @@

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.smallrye.mutiny.Multi;

public class MultipartFilenameTest {

Expand Down Expand Up @@ -72,6 +75,39 @@ void shouldUseFileNameFromAnnotationUsingString() {
assertThat(client.postMultipartWithPartFilenameUsingString(form)).isEqualTo("clientFile:file content");
}

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

ClientFormUsingByteArray form = new ClientFormUsingByteArray();
form.file = "file content".getBytes(StandardCharsets.UTF_8);
assertThat(client.postMultipartWithPartFilenameUsingByteArray(form)).isEqualTo("clientFile:file content");
}

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

ClientFormUsingInputStream form = new ClientFormUsingInputStream();
form.file = new ByteArrayInputStream("file content".getBytes(StandardCharsets.UTF_8));
assertThat(client.postMultipartWithPartFilenameUsingInputStream(form)).isEqualTo("clientFile:file content");
}

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

var list = new ArrayList<Byte>();
var array = "file content".getBytes(StandardCharsets.UTF_8);
for (var b : array) {
list.add(b);
}

ClientFormUsingMultiByte form = new ClientFormUsingMultiByte();
form.file = Multi.createFrom().items(list.stream());
assertThat(client.postMultipartWithPartFilenameUsingMultiByte(form)).isEqualTo("clientFile:file content");
}

@Test
void shouldCopyFileContentToString() throws IOException {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
Expand Down Expand Up @@ -121,7 +157,7 @@ public String upload(@MultipartForm FormData form) {
}

@POST
@Path("/using-string")
@Path("/using-form-data")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String uploadWithFileContentUsingString(@MultipartForm FormData form) throws IOException {
return form.myFile.fileName() + ":" + Files.readString(form.myFile.uploadedFile());
Expand Down Expand Up @@ -185,10 +221,25 @@ public interface Client {
String postMultipartWithPartFilename(@MultipartForm ClientFormUsingFile clientForm);

@POST
@Path("/using-string")
@Path("/using-form-data")
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartWithPartFilenameUsingString(@MultipartForm ClientFormUsingString clientForm);

@POST
@Path("/using-form-data")
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartWithPartFilenameUsingByteArray(@MultipartForm ClientFormUsingByteArray clientForm);

@POST
@Path("/using-form-data")
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartWithPartFilenameUsingInputStream(@MultipartForm ClientFormUsingInputStream clientForm);

@POST
@Path("/using-form-data")
@Consumes(MediaType.MULTIPART_FORM_DATA)
String postMultipartWithPartFilenameUsingMultiByte(@MultipartForm ClientFormUsingMultiByte clientForm);

@POST
@Path("/file-content")
@Consumes(MediaType.MULTIPART_FORM_DATA)
Expand Down Expand Up @@ -228,4 +279,31 @@ public static class ClientFormUsingString {
@PartFilename(FILE_NAME)
public String file;
}

public static class ClientFormUsingByteArray {
public static final String FILE_NAME = "clientFile";

@FormParam("myFile")
@PartType(MediaType.APPLICATION_OCTET_STREAM)
@PartFilename(FILE_NAME)
public byte[] file;
}

public static class ClientFormUsingInputStream {
public static final String FILE_NAME = "clientFile";

@FormParam("myFile")
@PartType(MediaType.APPLICATION_OCTET_STREAM)
@PartFilename(FILE_NAME)
public InputStream file;
}

public static class ClientFormUsingMultiByte {
public static final String FILE_NAME = "clientFile";

@FormParam("myFile")
@PartType(MediaType.APPLICATION_OCTET_STREAM)
@PartFilename(FILE_NAME)
public Multi<Byte> file;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ public QuarkusMultipartForm attribute(String name, String value, String filename
}

public QuarkusMultipartForm entity(String name, Object entity, String mediaType, Class<?> type) {
pojos.add(new PojoFieldData(name, entity, mediaType, type, parts.size()));
return entity(name, null, entity, mediaType, type);
}

public QuarkusMultipartForm entity(String name, String filename, Object entity, String mediaType, Class<?> type) {
pojos.add(new PojoFieldData(name, filename, entity, mediaType, type, parts.size()));
parts.add(null); // make place for ^
return this;
}
Expand Down Expand Up @@ -132,19 +136,22 @@ public void preparePojos(RestClientRequestContext context) throws IOException {
break;
}
}
parts.set(pojo.position, new QuarkusMultipartFormDataPart(pojo.name, value, pojo.mediaType, pojo.type));
parts.set(pojo.position,
new QuarkusMultipartFormDataPart(pojo.name, pojo.filename, value, pojo.mediaType, pojo.type));
}
}

public static class PojoFieldData {
private final String name;
private final String filename;
private final Object entity;
private final String mediaType;
private final Class<?> type;
private final int position;

public PojoFieldData(String name, Object entity, String mediaType, Class<?> type, int position) {
public PojoFieldData(String name, String filename, Object entity, String mediaType, Class<?> type, int position) {
this.name = name;
this.filename = filename;
this.entity = entity;
this.mediaType = mediaType;
this.type = type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ public class QuarkusMultipartFormDataPart {
private final Multi<Byte> multiByteContent;

public QuarkusMultipartFormDataPart(String name, Buffer content, String mediaType, Class<?> type) {
this(name, null, content, mediaType, type);
}

public QuarkusMultipartFormDataPart(String name, String filename, Buffer content, String mediaType, Class<?> type) {
this.name = name;
this.filename = filename;
this.content = content;
this.mediaType = mediaType;
this.type = type;
Expand All @@ -37,7 +42,6 @@ public QuarkusMultipartFormDataPart(String name, Buffer content, String mediaTyp
}
this.isObject = true;
this.value = null;
this.filename = null;
this.pathname = null;
this.text = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@ public FileUpload createFileUpload(HttpRequest request, String name, String file
if (formDataPart.isAttribute()) {
encoder.addBodyAttribute(formDataPart.name(), formDataPart.value());
} else if (formDataPart.isObject()) {
MemoryFileUpload data = new MemoryFileUpload(formDataPart.name(), "", formDataPart.mediaType(),
formDataPart.isText() ? null : "binary", null, formDataPart.content().length());
MemoryFileUpload data = new MemoryFileUpload(formDataPart.name(),
formDataPart.filename() != null ? formDataPart.filename() : "",
formDataPart.mediaType(),
formDataPart.isText() ? null : "binary",
null,
formDataPart.content().length());
data.setContent(formDataPart.content().getByteBuf());
encoder.addBodyHttpData(data);
} else if (formDataPart.multiByteContent() != null) {
Expand Down

0 comments on commit 4717c90

Please sign in to comment.