Skip to content

Commit

Permalink
Allow MultipartFormDataOutput to specify items with the same key
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand authored and danielsoro committed Sep 20, 2024
1 parent a6408d5 commit a5a8046
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public RestResponse<MultipartFormDataOutput> withFormDataOutput() {
MultipartFormDataOutput form = new MultipartFormDataOutput();
form.addFormData("name", RESPONSE_NAME, MediaType.TEXT_PLAIN_TYPE);
form.addFormData("part-with-filename", RESPONSE_FILENAME, MediaType.TEXT_PLAIN_TYPE, "file.txt");
form.addFormData("part-with-filename", RESPONSE_FILENAME, MediaType.TEXT_PLAIN_TYPE, "file2.txt");
form.addFormData("custom-surname", RESPONSE_SURNAME, MediaType.TEXT_PLAIN_TYPE);
form.addFormData("custom-status", RESPONSE_STATUS, MediaType.TEXT_PLAIN_TYPE)
.getHeaders().putSingle("extra-header", "extra-value");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public void testWithFormData() {
String body = extractable.asString();
assertContainsValue(body, "name", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_NAME);
assertContainsValue(body, "part-with-filename", MediaType.TEXT_PLAIN, "filename=\"file.txt\"");
assertContainsValue(body, "part-with-filename", MediaType.TEXT_PLAIN, "filename=\"file2.txt\"");
assertContainsValue(body, "custom-surname", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_SURNAME);
assertContainsValue(body, "custom-status", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_STATUS);
assertContainsValue(body, "active", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_ACTIVE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,27 @@ private void write(MultipartFormDataOutput formDataOutput, String boundary, Outp
throws IOException {
Charset charset = requestContext.getDeployment().getRuntimeConfiguration().body().defaultCharset();
String boundaryLine = "--" + boundary;
Map<String, PartItem> parts = formDataOutput.getFormData();
for (Map.Entry<String, PartItem> entry : parts.entrySet()) {
Map<String, List<PartItem>> parts = formDataOutput.getAllFormData();
for (var entry : parts.entrySet()) {
String partName = entry.getKey();
PartItem part = entry.getValue();
Object partValue = part.getEntity();
if (partValue != null) {
if (isListOf(part, File.class) || isListOf(part, FileDownload.class)) {
List<Object> list = (List<Object>) partValue;
for (int i = 0; i < list.size(); i++) {
writePart(partName, list.get(i), part, boundaryLine, charset, outputStream, requestContext);
List<PartItem> partItems = entry.getValue();
if (partItems.isEmpty()) {
continue;
}
for (PartItem part : partItems) {
Object partValue = part.getEntity();
if (partValue != null) {
if (isListOf(part, File.class) || isListOf(part, FileDownload.class)) {
List<Object> list = (List<Object>) partValue;
for (int i = 0; i < list.size(); i++) {
writePart(partName, list.get(i), part, boundaryLine, charset, outputStream, requestContext);
}
} else {
writePart(partName, partValue, part, boundaryLine, charset, outputStream, requestContext);
}
} else {
writePart(partName, partValue, part, boundaryLine, charset, outputStream, requestContext);
}
}

}

// write boundary: -- ... --
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.jboss.resteasy.reactive.server.multipart;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import jakarta.ws.rs.core.MediaType;
Expand All @@ -10,9 +12,26 @@
* Used when a Resource method needs to return a multipart output
*/
public final class MultipartFormDataOutput {
private final Map<String, PartItem> parts = new LinkedHashMap<>();
private final Map<String, List<PartItem>> parts = new LinkedHashMap<>();

/**
* @deprecated use {@link #getAllFormData()} instead
*/
@Deprecated(forRemoval = true)
public Map<String, PartItem> getFormData() {
Map<String, PartItem> result = new LinkedHashMap<>();
for (var entry : parts.entrySet()) {
if (entry.getValue().isEmpty()) {
continue;
}
// use the last item inserted as this is the old behavior
int lastIndex = entry.getValue().size() - 1;
result.put(entry.getKey(), entry.getValue().get(lastIndex));
}
return Collections.unmodifiableMap(result);
}

public Map<String, List<PartItem>> getAllFormData() {
return Collections.unmodifiableMap(parts);
}

Expand All @@ -31,7 +50,8 @@ public PartItem addFormData(String key, Object entity, MediaType mediaType, Stri
}

private PartItem addFormData(String key, PartItem part) {
parts.put(key, part);
List<PartItem> items = parts.computeIfAbsent(key, k -> new ArrayList<>());
items.add(part);
return part;
}
}

0 comments on commit a5a8046

Please sign in to comment.