'com.google.cloud.functions.HttpRequest instance does not support form-data with more than 1 file on the same field
I am creating a gcloud http Cloud Function which must receive a "form-data" form with a "medias" field which can contain up to 3 files, here is the basic code:
gcloud Java function :
package functions;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
public class HelloWorld implements HttpFunction {
@Override
public void service(HttpRequest request, HttpResponse response)
throws IOException {
var parts = request.getParts();
parts.forEach(
(e1, e2) -> {
System.out.println(e1);
System.out.println(e2.getFileName().get());
});
}
}
My simple request :
curl --location --request POST 'http://localhost:8080/' \
--form 'medias=@"/file1.jpeg"' \
--form 'medias=@"/file2.jpeg"'
Oddly, "request.getParts();" returns a "Map<String, HttpPart>" and therefore I don't see how to retrieve multiple files of the same submitted parameter. In debugging, I get a nice:
"java.lang.IllegalStateException: Duplicate key medias (attempted merging values com.google.cloud.functions.invoker.http.HttpRequestImpl$HttpPartImpl@49e848cf
and
com.google.cloud.functions.invoker.http .HttpRequestImpl$HttpPartImpl@6d5d7015)"
The same query works without problem with "Springboot" by specifying this as a parameter:
@RequestParam(value = "medias", required = false) MultipartFile[] medias
So do you think it's a bug with this dependency?
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.0.4</version>
<scope>provided</scope>
</dependency>
Solution 1:[1]
As mentioned in this GCP docs, you can process the data with a multipart/form-data content type by using the sample code below.
for (HttpRequest.HttpPart httpPart : request.getParts().values()) {
String filename = httpPart.getFileName().orElse(null);
if (filename == null) {
continue;
}
//do something here
}
I've done some tests using your scenario and tried manipulating the getParts() method using java.util.stream.Collectors to come up with a solution like Map<String, List<HttpPart>> but failed. It seems that it's not possible to use the same parameter key for each form-data.
I also checked the source code of the HttpRequest.getParts() method and found out that it has its own way of collecting the stream from the form-data that is failing when there's only a single key for each data as seen below:
public Map<String, HttpRequest.HttpPart> getParts() {
String contentType = this.request.getContentType();
if (contentType == null || this.request.getContentType().startsWith("multipart/form-data"))
throw new IllegalStateException("Content-Type must be multipart/form-data: " + contentType);
try {
return (Map<String, HttpRequest.HttpPart>)this.request.getParts().stream().collect(Collectors.toMap(Part::getName, x -> new HttpPartImpl(x)));
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (ServletException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
Finally, my suggestion is to use "unique" parameter key for each form-data.
curl --location --request POST 'http://localhost:8080/' --form 'medias[item][email protected]' --form 'medias[item1][email protected]'
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
