I'm trying to download an Excel file using RESTful call. I thought it should be pretty simple, but I keep getting HTTP 406 Not Applicable Eror. This is what my controller method looks like:
#RequestMapping(value = "/gettemplate", method = RequestMethod.GET, produces = "application/vnd.ms-excel")
#ResponseBody
public Response getExcelTemplate() throws Exception {
File templateFile = new File("TestFile.xlsx");
ResponseBuilder builder = Response.ok(templateFile,"application/vnd.ms-excel");
builder.header("Content-Disposition", "attachment; filename=\"" + templateFile.getName() + "\"" );
return builder.build();
}
I've tried setting request header to accept application/vnd.ms-excel, I've also tried using application/octet-stream instead of vnd.ms-excel. I get an HTML response back with 406 error message in either case. Here's what my Ajax test call looks like:
Ext.Ajax.request({
url: 'myservice/gettemplate',
//dataType: 'application/vnd.ms-excel',
headers: {
'Accept': 'application/vnd.ms-excel, text/plain, */*'
//'Content-Type': 'application/vnd.ms-excel'
},
success: function (response, options) {
alert(1);
},
failure: function (response, options) {
alert(2);
}
});
I've commented out the lines that I've tried and removed as it didn't help. This could be a very simple config change, but I can't seem to figure out.
When you annotate a request mapping return type with #ResponseBody, Spring will attempt to convert the object into the response using an HttpMessageConverter, as it says on the reference page:
#ResponseBody As with #RequestBody, Spring converts the returned object to a
response body by using an HttpMessageConverter.
You can see the list of available converters here: Message converters
It looks like the application/vnd.ms-excel you specify is not supported by any of the converters. Maybe that is why you get a 406.
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent
in the request.
The solution in your case is to remove the #ResponseBody annotation and handle the file download in another way.
See here for examples on how to download a file from a Spring controller:
Downloading a file from Spring controllers
Returning a file from a controller in Spring
Somewhat based on ESala's answer I tried a few things. Finally got it working by setting header and content type to HttpServletResponse. Here's the code that worked for me. This works in both IE and Firefox flawlessly with just a simple anchor tag, without even an ajax call. Hope it helps someone else.
#RequestMapping(value ="/gettemplate", method = RequestMethod.GET)
#ResponseBody
public void getExcelTemplate(HttpServletRequest request, HttpServletResponse response) throws Exception{
response.setContentType("application/vnd.ms-excel");
response.setHeader("content-disposition", "attachment; filename=TestFile.xlsx");
InputStream fis = getClass().getClassLoader().getResourceAsStream("templates/TestFile.xlsx");
int x = fis.available();
byte byteArray[] = new byte[x];
logger.info(" File size :"+byteArray.length);
fis.read(byteArray);
response.getOutputStream().write(byteArray);
response.flushBuffer();
fis.close();
}
Related
So, I am trying to make and upload file(s) functionality, from some blog posts and stackoverflow questions I got this solution:
#PostMapping("/file/upload")
fun handleFileUpload(
#RequestParam("files") files: List<MultipartFile>?,
): String {
if(files == null) {
return "File is null"
}
// SOME LOGIC
return "All Good it seems"
}
But when I send request trough postman I my files are null, here is postman request:
I saw some some solutions that use #RequestPart instead of #RequestParam, but when I go down that path, I get unsupported media type response.
PS: I use Java 11 and Spring Boot 2.6
UPDATE:
Applied suggestions from comments and ended up with this code:
#PostMapping("/file/upload", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
fun handleFileUpload(
#RequestPart("files") files: List<MultipartFile>?,
): String {
//SOME CODE
}
When I send request trough postman I get 415 Unsupported Media Type
You should use #RequestPart List<MultipartFile> and directly specify Content-Type in Postman
Postman screenshot
Adding to Revasha's response, you should also add
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
on your endpoint to avoid the 415 http error
I'm working on unit tests (backed by mocks) for my dropwizard resources. I recently added some response headers and different status codes, and want to test those.
Unfortunately I can't find an example in their documentation where they get both the entity and examine the response.
The method I'm testing streams back a file. The method itself is defined as:
#GET
#Path("/{assetId}")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
#PermitAll
public Response download(#Auth User user,
#PathParam("assetId") String assetId,
#HeaderParam("Range") String rangeHeader) {
To verify the download, I was initially making a request from my test like this:
final InputStream responseStream = resource.client()
.target("/assets/123")
.request()
.get(InputStream.class);
I could then write the stream to a file and validate that it was the file being returned from the resource. Worked fine. (Here, "resource" is dropwizard's ResourceTestRule.)
However, as above, I now want to get a handle on the response itself. There are examples floating around which use Jersey's ClientResponse, but that doesn't work (yet). I tried:
final ClientResponse response = resource.client()
.target("/assets/123")
.request()
.get(ClientResponse.class);
However, this ends up with an exception:
javax.ws.rs.client.ResponseProcessingException: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/octet-stream, type=class org.glassfish.jersey.client.ClientResponse, genericType=class org.glassfish.jersey.client.ClientResponse.
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:811)
at org.glassfish.jersey.client.JerseyInvocation.access$700(JerseyInvocation.java:92)
at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:701)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
...which I'm not sure what to do with. Any help?
Thanks
Ah, this was simpler than I thought.
final Response response = resource.client()
.target("/assets/123")
.request()
.get();
InputStream responseStream = (InputStream) response.getEntity();
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 pass data from one application (Spring MVC) to another application written with ZK Framework using POST request.
One of the parameters of the post method will be a file. Let's call it id.
So, what I do have now is Composer with the following code:
public class PictureComposer extends SelectorComposer<Component> {
#Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
Execution e = Executions.getCurrent();
System.out.println("post param: " + Executions.getCurrent().getParameterMap().get("id"));
HttpServletRequest request = (HttpServletRequest) e.getNativeRequest();
String idd = request.getParameter("id");
System.out.println("id: " + idd);
}
}
And, when I try to make a POST request to this composer, Executions.getCurrent().getParameterMap().get("id") seems to return null.
Same thing with the second approach (using HttpServletRequest).
On the other hand, I get some value for id, when I use GET request and pass this value as a parameter in the URL. But, unfortunately, I cannot pass my file in the URL.
So, the main question is how I can retrieve the variable from the POST request?
UPD: (for now I try to retrieve a plain string parameter, just as simplified example) My POST request looks like this:
POST /zssessentials/picture.zul HTTP/1.1
Host: localhost:8080
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="id"
wooooow
----WebKitFormBoundaryE19zNvXGzXaLvS5C
In order to retrieve an uploaded file, you should set enctype property of your <form> element to multipart/form-data, as below:
<form method="POST" action="your/zk/address" enctype="multipart/form-data" >
...
</form>
Then you can use the getPart method of HttpServletRequest class to access the uploaded file. So your code should be changed to something like this:
Part filePart = request.getPart("id");
InputStream filecontent = filePart.getInputStream();
I've figured out.
The reason was that the servlet didn't parse the multipart requests on its own.
I had been sending requests with this line in the header (even if it was plain string or integer values):
Content-type: multipart/form-data
and that was the reason.
So, I was expected to use the third-party library in the PictureComposer class.
In my case Streaming API from Apache came in handy.
Also, this StackOverflow answer helped me a lot in resolving my problem.
I'm at a loss for how to process results from a post in play framework. The following code works to process a GET response:
WSRequestHolder request = WS.url(myURL);
return async(
request.get().map(
new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
String json = response.getBody();
System.out.println("Json response: " + json);
//Do stuff
return ok(json);
}
}
)
);
I thought I'd just be able to replace .get() with post(myvalue). However, this does not work. What is the proper way to process a POST response in Play Framework?
WSRequestHolder.post(...) accepts 4 different types as a parameter: java.io.File, java.io.InputStream, JsonNode and String. You can replace .get() with .post(myvalue), depending on what myvalue is and what Content-Type myURL is expecting.
POSTing with those types will set the Content-Type header appropriate to the type. Passing a JsonNode will automatically set the Content-Type to application/json, for example. However, when passing a String you will likely have to set the Content-Type yourself, as Play isn't going to know whether that String is supposed to be JSON or a form.
From the Play Documentation:
If you're sending a form:
request.setContentType("application/x-www-form-urlencoded").post("key1=value1&key2=value2") ...
Or posting JSON as a String:
request.setContentType("application/json").post(jsonString) ...
If you're having other compile errors, please post them.