How to receive 2 binary files and JSON in Jersey jax-rs? - java

I need to build a service that can receive 2 binary files (~100k each) and some metadata, preferably in json.
I found this, but it only seems to provide one InputStream to one of the parts. But I'd need two.. so what to do?

You have a few options
Simply add another parameter(s) with a different part annotation
#POST
#Consumes("multipart/form-data")
public Response post(#FormDataParam("file1") InputStream file1,
#FormDaraParam("file2") InputStream file2) {
}
The parts can have the same part name, so you could do
#POST
#Consumes("multipart/form-data")
public Response post(#FormDataParam("file") List<FormDataBodyPart> files) {
for (FormDataBodyPart file: files) {
FormDataContentDisposition fdcd = file.getFormDataContentDisposition();
String fileName = fdcd = getFileName();
InputStream is = file.getValueAs(InputStream.class);
}
}
You could traverse the entire multipart body youself
#POST
#Consumes("multipart/form-data")
public Response post(FormDataMultiPart mulitPart) {
Map<String, List<FormDataBodyPart>> fields = multiPart.getFields();
}
See Also:
Sending multiple files with Jersey: MessageBodyWriter not found for multipart/form-data, for a complete example
File upload along with other object in Jersey restful web service, for how the handle the JSON as a POJO.

Related

How multipart form data upload handles large file

I am using quarkus framework for java application.I have created 2 rest apis for consuming file data.
#POST
#Path("file")
#Consumes(MediaType.APPLICATION_OCTET_STREAM)
#Produces(MediaType.APPLICATION_JSON)
public Response uploadFile(byte[] fileData) {
System.out.println("Received file of size = " + fileData.length);
String s = new String(fileData);
return Response.ok().build();
}
#POST
#Path("files")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public void uploadFile(#MultipartForm FormData fileData) throws IOException {
System.out.println("Received file of size = ");
System.out.println(fileData.file.length());
return Response.ok().build();
}
FormData class looks like this
public class FormData {
#FormParam("file")
#PartType(MediaType.APPLICATION_OCTET_STREAM)
public File file;
}
While both of these rest endpoint works fine for small files. But if i upload file as big as 700MB first endpoint fails with OOM issue while file upload with multipart-form succeed. Can someone explain how memory is managed in case of multipart-form upload?
The second API saves the file to a temp file on the file system. (which I believe is being automatically deleted when the call completes and the File is closed)
The first API saves the file to a byte stream (array) in memory - that's why you're getting an OOM error.

Spring doesn't return JSON in full

I am using spring boot and #PostMapping trying to return a POJO that contains 1 Multipart file and some String. When i look at Postman i only see half of the Multipart object. File is 3kb. I don't get any errors. When i return the multipart variable null other variables in JSON are being shown in response so they are not empty. How can i return all of the JSON?
public class foo{
public MultipartFile dataFile;
public String project;
public Boolean extract;
... getter - setter - constructor
}
I send it like
#PostMapping
public foo route(#RequestParam("dataFile") MultipartFile dataFile, ... ) {
...
return fooObject;
}
Response
{
"dataFile": {
"name": "dataFile",
"bytes":"MIKCAQYJKoZIhvcNAQcCoIKB8jCCge4CA... (half of the file)
As I thought, the MultipartFile is used to upload object, not to download it. As stated in the Javadoc:
A representation of an uploaded file received in a multipart request.
Which means, it is great for upload, but that is not the case for download.
The easiest way (and the most straightforward) would be to change the MultipartFile to a byte[] and send that to the client.
Here is an example:
public Foo getFile(MultipartFile multipartFile) {
byte[] bytes = multipartFile.getBytes();
return new Foo(bytes, "project");
}

How to use RESTEasy Client-Proxy with MultipartFile

I tried for several days to use the RESTEasy Client-Proxy with Multipart forms.
In the best scenario, I would like to pass a MultipartFile into the Proxy.
E.g.
//client:
//Resteasy proxy creation left out for brevity
public Response add(MultipartFile versionFile) {
proxy.add(versionFile);
}
//server (interface):
#POST
#Consumes({MediaType.MULTIPART_FORM_DATA})
FormularDTO add(MultipartFile versionFile);
This always ends in an Exception.
could not find writer for content-type multipart/form-data type: org.springframework.web.multipart.support
As suggested by the Docs, there a two ways to handle Multipart-Files:
a) MultipartOutput/MultipartInput:
What should I send via the Proxy? If I send a MultipartOutput, I get the same Exception. MultipartInput is Abstract.
b) Use DTO with #MultipartForm
The solution currently used in the project, but requires to map all File-Metadata, create a new DTO, etc.
See Example below:
//DTO
public class MultipartFileDataDTO {
#FormParam("file")
#PartType(MediaType.APPLICATION_OCTET_STREAM)
private InputStream file;
#FormParam("contentType")
#PartType(MediaType.TEXT_PLAIN)
private String contentType;
...
}
//Server-Interface
#POST
#Consumes({MediaType.MULTIPART_FORM_DATA})
FormularDTO add(#MultipartForm MultipartFileDataDTO versionFile);
//Client-Mapping
MultipartFileDataDTO upload = new MultipartFileDataDTO();
upload.setFile(versionFile.getInputStream());
upload.setContentType(versionFile.getContentType());
...
My Question: What is the easiest way to "pass" a MultipartFile via a generated RESTEasy-Client-Proxy?
I think the easiest way to do would be to create a simple MultiplartFormDataOutput object and send it to the proxy.
Here is a simple example:
MultipartFormDataOutput output = new MultipartFormDataOutput();
// It is possible to pass a File object or a InputStream in the addFormData
output.addFormData("file", fileObject, MediaType.APPLICATION_OCTET_STREAM_TYPE, filename);
proxy.add(output)

Jersey REST image GET issue

i have a simple question.
Lets say that i want to download multiple specific images when i call my GET method in my REST API from a directory, given that i have their names. eg.
../folder
name1.png
name2.png
name3.png
name4.png
And i want to download name2.png, name3.png.
Everything that i could dig up was regarding only 1 image per call, like this:
#Path("/image")
public class ImageService {
private static final String FILE_PATH = "c:\\picture.png";
#GET
#Path("/get")
#Produces("image/png")
public Response getFile() {
File file = new File(FILE_PATH);
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition",
"attachment; filename=image_from_server.png");
return response.build();
}
}
The thing that has come to my mind is to send a zip or something like that. Could anyone tell me if this is possible, and if it is how to do it? Thanks.

How to calculate MD5 hash of multipart request body using Jersey

I am using Jersey and I need to calculate the MD5 hash of the body of a multipart post request. My current resource method signature looks like this:
#POST
#Consumes("multipart/form-data")
#Produces("application/json")
public String post(
#FormDataParam("name") String name,
#FormDataParam("description") String description,
#FormDataParam("iconfile") FormDataBodyPart part,
#Context HttpServletRequest hsr) {
// ...
}
I did not find a way to get the raw request body, that I need to calculate the MD5 hash. When my resource method is invoked the input stream from the HttpServletRequest (hsr.getInputStream()) is already consumed and I can not read it again.
I tried changing my method signature to the following:
#POST
#Consumes("multipart/form-data")
#Produces("application/json")
public String test(byte[] bytes) {
// ...
}
This way I get the raw bytes of the request body and I can successfully calculate the MD5 hash but I don't know how to handle the multipart request from there (split the parts, get each part, etc.). Do I have to resort to handle the raw request myself? Or can I let Jersey do the dirty job and extract the FormDataParams for me and let me calculate the MD5 hash somehow?
Thanks,
This is what I ended up doing:
I created a container request filter that consumes the entity input stream, calculates the MD5 checksum and sets the entity input stream again so it can be consumed by Jersey to handle the multipart request and extract the FormDataParams for me.
I also injected the HttpServletRequest both in my filter and my resource method to communicate data between the two.
This is the filter class:
public class MD5CheckFilter implements ContainerRequestFilter {
#Context HttpServletRequest hsr;
public ContainerRequest filter(ContainerRequest request) {
byte[] bytes = request.getEntity(byte[].class); // this consumes the entity input stream
String contentMD5 = calculateMD5(bytes);
hsr.setAttribute("contentMD5", contentMD5);
// set the entity input stream so it can be consumed again
request.setEntityInputStream(new ByteArrayInputStream(bytes));
return request;
}
}
This is the relevant section of my web.xml within the servlet section:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>path.to.MD5CheckFilter</param-value>
</init-param>
This way I don't need to change the original method signature:
#POST
#Consumes("multipart/form-data")
#Produces("application/json")
public String post(
#FormDataParam("name") String name,
#FormDataParam("description") String description,
#FormDataParam("iconfile") FormDataBodyPart part,
#Context HttpServletRequest hsr) {
// ...
}

Categories

Resources