I need to write a REST api which would send an XML file as response.
I am confused with what MediaType should be defined as the response.
#Produces(MediaType.APPLICATION_OCTET_STREAM)
or
#Produces(MediaType.APPLICATION_XML)
The API will be served from a GET request and I want the file to be downloaded in the client side.
I will go with #Produces(MediaType.APPLICATION_XML), as the #Produces(MediaType.APPLICATION_OCTET_STREAM) is a constant for application/octet-stream which is usually used for binary type (such as video, music and stuff)
I suggest dont send file directly.
read file at server end and send the file content as a response.
so it will look like
#GET
#Produce("application/xml")
public Response sendData(){
//code to read file and store in a string object
return Response.built.ok(string object);
}
Related
I have the controller shown below:
#RequestMapping(value = "/videos/{id}",
headers = "Accept=image/jpeg, image/jpg, image/png, video/mp4",
method = RequestMethod.GET)
public ResponseEntity<byte[]> loadVideo(#PathVariable("id") long campaignId,
Principal principal) throws IOException {
This controller returns a byte stream of the media associated with the given id. It works fine. The only issue I'm having is loading this videos associated metadata (title, description, view count, etc...) as I'm sending back an array of bytes, so I'm not too sure where to put the meta data.
Should I place the metadata in the response headers?
Should I have two separate calls, one for the video (byte steam) and
another call which returns an object containing the meta data?
Or is there a better way to go about this than either of the two
options above?
As my comment was already lenghty I decided to repost it here again:
If you deal with certain media-types like image/jpg or video/mp4 you should include the metadata as headers as the payload of the response should only include the bytes of the respective file. This also enables a lookup of the metadata without having to download the bytes of the actual file via a simple HEAD request.
Certain API provides, howerver, define their own media-type or send a JSON or XML based response to the client. In this cases, the payload contains often a predefined structure which includes the bytes of the file as a base64 encoded string as well as the metadata as plain text. These APIs argument that sending multiple files at once is easier this way then to handle multipart content.
I know how to create endpoints that are handling files using MediaType.MULTIPART_FORM_DATA and #FormDataParam("file") FormDataBodyPart bodyPart, but I was wondering if I can also have JSON data along that request? Something like:
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(#RequestBody SomeModel someModel,
#FormDataParam("file") FormDataBodyPart bodyPart) {
return null;
}
At the moment if I add some JSON data on the "raw" tab on the following Postman request I'm getting HTTP 415 Unsupported Media Type probably because I specified that I consume MULTIPART_FORM_DATA but I'm also using #RequestBody which is looking for JSON content which is APPLICATION_JSON. So how can I have JSON data and a file handled in the same request? I know that it's possible to do that in two requests, I just want to do it in one if possible?
Why are you using both Spring and Jersey annotations? You should stick to using the annotations meant for the framework. Since you are using Jersey, should stick to its its annotations.
So here are the things to consider about your current code and environment.
There can't be two separate bodies. With your code, that's what it appears you expect to happen.
You can though put the JSON as part of the multi-part body. For that you should also annotate the SomeModel with the Jersey #FormDataParam
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(
#FormDataParam("model") SomeModel someModel,
#FormDataParam("file") FormDataBodyPart bodyPart) {
}
In the Jersey configuration, you need to make sure to register the MultiPartFeature. If you don't the body won't be able to be deserialized, and you will get exceptions and error responses.
Now the Postman problem. You can see similar problem here. The problem was that the Content-Type was not set for the JSON body part. For example the body might look something like
--AaB03x
Content-Disposition: form-data; name="model"
{"some":"model", "data":"blah"}
--AaB03x
Content-Disposition: form-data; name="file"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
You can actually see the body, if you hit the Preview button in Postman. The problem is that there is no Content-Type for the "model" part, as you can see in the "file" part. This happens because you can't set individual parts' Content-Type in Postman. The one that you will see will be discovered from the file extension. For example a .txt file will make Postman set the Content-Type to text/plain and a .png file to image/png.
If you look in the link above, I proposed maybe you could use a .json file instead of typing in the data. Of course that was just a theory. I didn't actually test it.
In any case, the Content-Type must be set in order for Jersey to be able to know to deserialize it as JSON. If the .json file extension theory doesn't pan out, then you can use a different client, like cURL, which I showed an example in the link, or you can use the Jersey client to test, as seen here.
Don't set the Content-Type header to multipart/form-data in Postman. It sets it for you when you use the form-data. I just saw a post where someone said there is bug when you set the header. Can't find the post now, and not something I've confirmed, but I'd just leave it out.
UPDATE
So the OP was able to find a way to set the Content-Type: application/json to the "model" part. But it is sometimes the case where with a Javascript client, you are not able to set it. So there will be no Content-Type. If this is the case, Jersey will not be able to deserialize the JSON, as it has no idea that it is actually JSON being sent. If you absolutely can't or have no idea how to set the Content-Type for individual parts, you could resort to doing the following.
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(#FormDataParam("model") FormDataBodyPart jsonPart,
#FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
SomeModel model = jsonPart.getValueAs(SomeModel.class);
}
Yes, you can get that as multipart form data.
you get like this in angularjs:
$scope.uploadFile = function () {
var file = $scope.selectedFile[0];
$scope.upload = $upload.upload({
url: 'api/upload',
method: 'POST',
data: angular.toJson($scope.model),
file: file
}).progress(function (evt) {
$scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total, 10);
}).success(function (data) {
//do something
});
};
$scope.onFileSelect = function ($files) {
$scope.uploadProgress = 0;
$scope.selectedFile = $files;
};
public Response uploadFileAndJSON(#RequestParam("data") String data,
#MultiPartFile("file")File file) {
you can data as form data and convert it
like you want to your object using Gson jar.
return null;
}
Have a look at it for angularjs code:
Angularjs how to upload multipart form data and a file?
https://puspendu.wordpress.com/2012/08/23/restful-webservice-file-upload-with-jersey/
I want to send a folder or list of files in the particular folder as response to the client via REST using java. My server side is EJB.
I am trying with following code. But got FileNotFoundException
#GET
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFiles() {
File file = new File("C:\Documents and Settings\Administrator\Desktop\MyFolder");
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM).build();
}
Tried with the following code. But got NullPointerException
#GET
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFiles() {
File file = new File("C:\Documents and Settings\Administrator\Desktop\MyFolder");
ResponseBuilder response = Response.ok(file.listFiles());
response.header("Content-Disposition", "inline; filename=units.zip");
return response.build();
}
Working in a windows machine.
Can anyone give me any advice, or examples of working code to conform to?
Either use forward slashes or double backslashes in the file name.
Also, don't expect the folder to be zipped automagically. You'll have to create the ZIP archive and pass it to the Response.
My main question is how can I pass JSON as well as File to post request to REST API? What needs in Spring framework to work as client and wait for response by passing post with JSON and File?
Options:
Do I need to use FileRepresentation with ClientResource? But how can I pass file as well as JSON?
By using RestTemplate for passing both JSON as well as File? How it can be used for posting JSON as well as File?
Any other option is available?
Sounds like an awful resource you're trying to expose. My suggestion is to separate them into 2 different requests. Maybe the JSON has the URI for the file to then be requested…
From a REST(ish) perspective, it sounds like the resource you are passing is a multipart/mixed content-type. One subtype will be application/json, and one will be whatever type the file is. Either or both could be base64 encoded.
You may need to write specific providers to serialize/deserialize this data. Depending on the particular REST framework, this article may help.
An alternative is to create a single class that encapsulates both the json and the file data. Then, write a provider specific to that class. You could optionally create a new content-type for it, such as "application/x-combo-file-json".
You basically have three choices:
Base64 encode the file, at the expense of increasing the data size
by around 33%.
Send the file first in a multipart/form-data POST,
and return an ID to the client. The client then sends the metadata
with the ID, and the server re-associates the file and the metadata.
Send the metadata first, and return an ID to the client. The client
then sends the file with the ID, and the server re-associates the
file and the metadata.
How to download PDF file with other data on the same HTTP request to a Servlet?
For example, I have a PDF file in my server and I want to respond to a request with that PDF file and other data like myname and myage, etc on the same request. Can it be done on the same request?
The Java Servlet API does not provide any in-built mechanism for multi-part responses (which is the name of the feature that you are looking for). The Servlet API documentation hints at how this can be achieved, in the ServletResponse API doc:
To send binary data in a MIME body
response, use the ServletOutputStream
returned by getOutputStream(). To send
character data, use the PrintWriter
object returned by getWriter(). To mix
binary and text data, for example, to
create a multipart response, use a
ServletOutputStream and manage the
character sections manually.
Multipart responses are created by setting the content-type (the MIME type) of the response to "multipart/x-mixed-replace;boundary=xyz". The value xyz is arbitrary and is used to delineate the several sections of the response. An implementation of a Multipart Response class can be found in the book - "Java Servlet Programming" by Jason Hunter, and also in the KickJava site (please read the license before using it in your project).
If this other data is just text, perhaps you could it include it as headers of the response