Java Jax-rs - get only file body in payload - remove headers - java

I have to upload a binary file to my server using UI/Postman client. My backend code for Rest API is:
#POST
#Produces({JSONHeaders.MEDIA_TYPE_JSONAPI, MediaType.APPLICATION_JSON})
#Path("loadLicense")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response loadLicense2(#ApiParam("load a license") File input) {
....
}
But the file I get has header details added to it which I don't need. File content is something like:
----------------------------013134317098674079511595^M
Content-Disposition: form-data; name="file"; filename="license.lic"^M
Content-Type: application/octet-stream^M
^M
^#^#^T.^#^#m.....^#^#^#^K^#^A^#^#^#^#^C^#^#^#(^#.^E^#^#^#^K^#.^#^#^#^G.^#^#^#^K^#.^#^#^#^#^B^#^#^#^K^#.^#^#^#^#^#^#^#^#^K^#.^#^#^#^#^F^#^#^#^K^#.^#^#^
^#^#^#^U^#^N^BGR^#^#^#^#^M^#^G^Bc^#^#^#^#^K^#^O^B1.0^#^#^#^#^Q^#4^Bpermanent^#^#^#^#^G^#.
^G.^V......I..^HC_.^^.^U...Y..G.^K.R.^?^O&..^.{V.Z.......h^B.<^O....w'#bk.^B]..*...8.W93...Z.\..... ..g.a+.....,^M
----------------------------013134317098674079511595--^M
But I just need the binary content. Is there any way to do that?
Note: I tried #FormParam - it doesn't work and I get this error
The #FormParam is utilized when the content type of the request entity is not application/x-www-form-urlencoded]
Tried #FormDataParam - not able to resolve it in code.

Instead of #FormParam you might need to use #FormDataParam, depending on how you send the data via Postman. They have different purposes, i.e. are for use with different MIME types:
#FormParam is intended to be used with MIME type application/x-www-form-urlencoded (constant MediaType.APPLICATION_FORM_URLENCODED)
#FormDataParam is intended to be used with multipart/form-data (constant MediaType.MULTIPART_FORM_DATA)
The following snippet expects the file being sent via form data:
#POST
#Produces({JSONHeaders.MEDIA_TYPE_JSONAPI, MediaType.APPLICATION_JSON})
#Path("loadLicense")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response loadLicense2(
#FormDataParam("file") InputStream istream,
#FormDataParam("file") FormDataContentDisposition disp) {
}
The input stream contains the binary data, the second parameter gives you some information about the uploaded file, e.g. the file name.
You need the jersey-media-multipart artifact for this:
https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>3.0.2</version>
</dependency>

Related

Convert multipart body to java object

I'm trying to use java + camel to read a multipart file and converting it to object to process it.
My multipart file is composed of 2 kind of datas: an application/json and an application/octet-stream
Is there a way to convert it to some kind of object that i can work with?
basically I'm sending a get REST request to an application that respondes with a multipart file. I can't figure it out how to convert the response body: the application/json to my POJO and the application/octet-stream to a DataHandler
To handle multipart in camel, you can use unmarshal().mimeMultipart(). After this line, Camel Exchange contains an AttachmentMessage, the body of it is the first part of the multipart. The next parts can be obtained by calling attachmentMessage.getAttachmentObjects() in Camel processor. For more information on how unmarshal().mimeMultipart() works, see the source code of method unmarshall() in org.apache.camel.dataformat.mime.multipart.MimeMultipartDataFormat.
Further actions depend on your case. For example, if the first part of a multipart always contains a json object of your SimplePojo class, then you can use unmarshal().json(SimplePojo.class) immediately after unmarshal().mimeMultipart(). Then Camel body will contain your pojo. DataHandlers for the next parts can be obtained as follows:
DataHandler dataHandler = attachmentObjects.get("test.txt").getDataHandler();
// test.txt comes from the filename field of the Content-Disposition header in case you have multipart/form-data.
Below is an example of processing a response from a REST service located at {{http.url}}, in a multipart format of the following form:
--Boundary_2411_1961957611_1491990591774
Content-Disposition: form-data; name="part1"
Content-Type: application/json; charset=utf-8
{
"id": 123,
"name": "simple pojo"
}
--Boundary_2411_1961957611_1491990591774
Content-Disposition: form-data; name="part2"; filename="test.txt"
Content-Type: application/octet-stream
test
--Boundary_2411_1961957611_1491990591774--
Camel RouteBuilder:
from("direct:start")
.setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.GET))
.to("{{http.url}}")
.unmarshal().mimeMultipart()
.unmarshal().json(SimplePojo.class)
.process(exchange -> {
AttachmentMessage attachmentMessage = exchange.getMessage(AttachmentMessage.class);
SimplePojo simplePojo = attachmentMessage.getBody(SimplePojo.class);
Map<String, Attachment> attachmentObjects = attachmentMessage.getAttachmentObjects();
DataHandler dataHandler = attachmentObjects.get("test.txt").getDataHandler();
})
.to("mock:end");
SimplePojo:
public class SimplePojo {
private Long id;
private String name;
//...
//getters, setters
}
In order for unmarshal().mimeMultipart() and unmarshal().json(SimplePojo.class) to work, you must have dependencies:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mail</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<version>${camel.version}</version>
</dependency>

Is it possible to send two different Content-Type for downloading file using jax-rs POST method

Is it possible to send two different Content-Type for POST method using post-man? Like, if the service is used for downloading excel file so in that case, #Consumes(MediaType.APPLICATION_JSON) is used for sending some user detail which is json structure and #Produces(MediaType.APPLICATION_OCTET_STREAM) is used for sending back the response as file.
NOTE: I don't want to use form-data, that is a constraint for this service.
On the client request, the Content-Type header is only for the type of data that is in the entity body. The Accept header is what you send for the type of data you want back.
On the server side, it is perfectly fine to do the following.
#POST
#Path("something")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response post(Model model) throws Exception {
final InputStream in = new FileInputStream("path-to-file");
StreamingOutput entity = new StreamingOutput() {
#Override
public void write(OutputStream out) {
IOUtils.copy(in, out);
out.flush();
}
};
return Response.ok(entity)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment;filename=somefile.xls")
.build();
}
The request to this endpoint should look like
POST /api/something HTTP 1.1
Content-Type: application/json;charset=utf-8
Accept: application/octet-stream
{"the":"json", "entity":"body"}
See also:
This post about purpose of #Produces and #Consumes and the role they play in content negotiation.
Aside
As an aside, consider using application/vnd.ms-excel as the Content-Type instead of application/octet-stream. This is already the standard MIME type for Microsoft Excel files1. When using StreamingOutput as the response entity type, you can make the #Produces anything you want, as there is no real conversion needed.
In Postman, when you use the "Send and download" feature, you will notice that when the Content-Type is application/octet-stream, it will suggest the file extension of .bin when saving the file. But if you have the Content-Type as application/vnd.ms-excel, then the suggested extension is .xls, which is what it should be.
1. Incomplete list of MIME types

Uploading JSON and binary file in one request

I am looking to create a RESTful API for use with an Android and iOS app. So far I have been experimenting with using Jersey on the server and then the appropriate http libraries on the client side. At the moment I have been using multipart/related as the mimetype for the request with JSON forming the first part of the body then a jpeg image as the second.
So far I have had problems with making the request to the server, getting a 406 Not Acceptable from Jersey. I note that multipart/related is primarily used in sending emails. Is there actually a way that I can support mixed type content as an upload or have I entirely mis-understood the usage of multipart/related in this context?
You may want to look at this blog, for more information, but here is the important part to help you along:
http://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
#FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("file") FormDataContentDisposition fileDetail) {
String uploadedFileLocation = "d://uploaded/" + fileDetail.getFileName();
// save it
writeToFile(uploadedInputStream, uploadedFileLocation);
String output = "File uploaded to : " + uploadedFileLocation;
return Response.status(200).entity(output).build();
}
I expect you want multipart/form-data instead, as this is part of the description of multipart/related:
The Multipart/Related media type is intended for compound objects
consisting of several inter-related body parts. For a
Multipart/Related object, proper display cannot be achieved by
individually displaying the constituent body parts. The content-type
of the Multipart/Related object is specified by the type parameter.
The "start" parameter, if given, points, via a content-ID, to the
body part that contains the object root. The default root is the
first body part within the Multipart/Related body.
For more on this mime type you can look at
https://www.rfc-editor.org/rfc/rfc2387
If you are wanting to submit image along with the json body, you can base64 encode the image and include the base64 string in the json. Then on the server side, you base64 decode the string and upload the image file to the blobstore. See the file upload example (at the bottom of the page) here https://developers.google.com/appengine/docs/java/blobstore/overview
Alternatively, you could do a separate upload to the blobstore and get the blobkey for the uploaded image. You can then include the blobkey in the json body that you post to the server.Using this approach you would need to get the uploadurl every time you need to do a new image upload.

REST - HTTP Post Multipart with JSON

I need to receive an HTTP Post Multipart which contains only 2 parameters:
A JSON string
A binary file
Which is the correct way to set the body?
I'm going to test the HTTP call using Chrome REST console, so I'm wondering if the correct solution is to set a "label" key for the JSON parameter and the binary file.
On the server side I'm using Resteasy 2.x, and I'm going to read the Multipart body like this:
#POST
#Consumes("multipart/form-data")
public String postWithPhoto(MultipartFormDataInput multiPart) {
Map <String, List<InputPart>> params = multiPart.getFormDataMap();
String myJson = params.get("myJsonName").get(0).getBodyAsString();
InputPart imagePart = params.get("photo").get(0);
//do whatever I need to do with my json and my photo
}
Is this the way to go?
Is it correct to retrieve my JSON string using the key "myJsonName" that identify that particular content-disposition?
Are there any other way to receive these 2 content in one HTTP multipart request?
If I understand you correctly, you want to compose a multipart request manually from an HTTP/REST console. The multipart format is simple; a brief introduction can be found in the HTML 4.01 spec. You need to come up with a boundary, which is a string not found in the content, let’s say HereGoes. You set request header Content-Type: multipart/form-data; boundary=HereGoes. Then this should be a valid request body:
--HereGoes
Content-Disposition: form-data; name="myJsonString"
Content-Type: application/json
{"foo": "bar"}
--HereGoes
Content-Disposition: form-data; name="photo"
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
<...JPEG content in base64...>
--HereGoes--

How do I remove http artifacts from a multipart form-data request?

I have this method signature in a jersery servlet. The servlet is being reached and the form data is present in the uploadedInputStream object, but the stream does not have the http artifacts removed from it. See below.
#POST
#Produces("text/plain")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public String uploadFileIE(
#FormDataParam("qqfile") InputStream uploadedInputStream ){
}
When saved to a file, the input stream has these artifacts surrounding the byte data:
-----------------------------7dc1f42e3005a8
Content-Disposition: form-data; name="qqfile";filename="[filename]"
Content-Type: application/octet-stream
[bytes from data stream]
-----------------------------7dc1f42e3005a8--
Shouldn't these artifacts be removed already at this point? Is there any easy way to remove them without re-inventing the wheel?
With Jersey you need to 'consume' the extra header information with a FormDataContentDisposition object. Messy but necessary:
#POST
#Produces("text/plain")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public String uploadFileIE(
#FormDataParam("qqfile") InputStream uploadedInputStream,
#FormDataParam("qqfile") FormDataContentDisposition fileDetail){
}

Categories