My payload has the Content-Disposition field. I am trying to upload a bpmn file. So at backend i need to parse the input stream and extract this file name. but i am not able to find a solution of this. I am using jesrey for rest.
I tried with :
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response addWorkflowSchema(#FormDataParam("bpmndata") InputStream uploadedInputStream,
String filename,
#Context HttpServletRequest request)
to get whole payload data. but only if i remove FormDataParam i ma getting request body.
Reuqest Payload :
------WebKitFormBoundary8CCb878TyZksE9go
Content-Disposition: form-data; name="bpmndata"; filename="process.bpmn"
Content-Type: application/octet-stream
------WebKitFormBoundary8CCb878TyZksE9go--
I need to get filename from Content-Disposition. How can i get this.
Just inject the FormDataContentDisposition also, and get the file name from that.
public Response addWorkflowSchema(
#FormDataParam("bpmndata") InputStream in,
#FormDataParam("bpmndata") FormDataContentDisposition fdc) {
String fileName = fdc.getFileName();
}
The InputStream will only be the content of the file part. It won't include the headers, so you don't need to extract anything from it.
Related
My API returns an Excel (xslx) File for a GET Request. If a "Accept" Header is present, the binary result gets encoded/corrupt. I can send the request using fiddler with the minimum Headers required and everything works just fine. If i add an Accept Header like a browser does:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
The result gets larger (8kb instead of 4kb) and seems to be encoded in some way. Fiddler detects this encoding and asks to decode it. After decoding so, the result is valid again. When i use chrome browser instead, it downloads the larger (8kb) file, not decoded and therefor corrupt.
#GET
#Path("/export-report")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
//#Produces({ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" })
public Response exportReport() throws IOException {
byte[] fileBytes = getFileBytes();
String filename = "report.xslx";
String mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
return Response.ok()
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
.header("Content-Length", fileBytes.length)
.entity(fileBytes)
.header("Content-Type", mimeType)
.build();
}
I tried returning a File, a InputStream and a byte Array, it didn't change a thing.
I also had a look at
Input and Output binary streams using JERSEY?
I have no soltuion, any idea?
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 am trying to download a file from a REST service using JAX-RS.
This is my code which invokes the download by sending a GET request:
private Response invokeDownload(String authToken, String url) {
// Creates the HTTP client object and makes the HTTP request to the specified URL
Client client = ClientBuilder.newClient();
WebTarget target = client.target(url);
// Sets the header and makes a GET request
return target.request().header("X-Tableau-Auth", authToken).get();
}
However I am facing problems converting the Response into an actual File object. So what I did is the following:
public File downloadWorkbook(String authToken, String siteId, String workbookId, String savePath)
throws IOException {
String url = Operation.DOWNLOAD_WORKBOOK.getUrl(siteId, workbookId);
Response response = invokeDownload(authToken, url);
String output = response.readEntity(String.class);
String filename;
// some code to retrieve the filename from the headers
Path path = Files.write(Paths.get(savePath + "/" + filename), output.getBytes());
File file = path.toFile();
return file;
}
The file which is created is not valid, I debugged the code and noticed that output contains a String like that (much larger):
PK ͢�F���� �[ Superstore.twb�ysI�7����ߡ���d�m3��f���
Looks like binary. Obviously there is something wrong with the code.
How do I get the HTTP response body as a string from the Response object?
Edit:
Quote from the REST API reference about the HTTP response:
Response Body
One of the following, depending on the format of the workbook:
The workbook's content in .twb format (Content-Type: application/xml)
The workbook's content in .twbx format (Content-Type: application/octet-stream)
As you noticed yourself, you're dealing with binary data here. So you shouldn't create a String from your response. Better get the input stream and pipe it to your file.
Response response = invokeDownload(authToken, url);
InputStream in = response.readEntity(InputStream.class);
Path path = Paths.get(savePath, filename);
Files.copy(in, path);
1) I assume by this point you're clear on the difference between "binary file" and "text file". And that you can only capture the latter into a "string".
2) Sebastian gave you excellent advice for capturing a binary file (+1, Sebastian!). VERY IMPORTANT: you should always set the MIME type (Content-Type: xxx/yyy)in cases like this. Here is another link that might be useful.
3) Finally, there are cases where you might WANT to treat "binary" data as text. This is how e-mail attachments work with SMTP (a text protocol). In these cases, you want to use Base64 Encoding. For example: JAX-RS | Download PDF from Base64 encoded data
I'm implementing a file upload in a web application.
The front-end is written in angularJS and uses the angular-file-upload package (the one that can be found at this link https://github.com/nervgh/angular-file-upload).
The back-end is Java / Jersey Web Services.
My Question is:
The uploaded file contains a WebKitFormBoundary header and footer, like this:
------WebKitFormBoundarylqskdjlqksdjl
Content-Disposition: form-data; name="upload"; filename="foo.bar"
Content-Type: multipart/form-data
Therefore, I'm not sure whether I'm uploading a file or a request. And of course, my back-end application considers that the uploaded files are corrupted and would not display them unless those lines are removed (for now manually).
Bottom line is : how do I get rid of that header and footer in the uploaded file?
Here are some code samples.
Front-End
Once again: angularJS angular-file-upload
item.headers = {
'Content-Disposition': 'attachment; filename="' + item.file.name + '"',
'Content-Type': 'multipart/form-data'
};
Back-End
and Java / Jersey
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("someurl/{fileName}")
public Artifact uploadArtifact(InputStream uploadedStream, #PathParam("fileName") String fileName) throws Exception;
Note
I'm wondering if the Content-Disposition: attachment in my angularJS part could be what's messing it up?
And that it should rather be Content-Disposition: form-data?
Thx in advance!
You need to place #FormDataParam annotation in order to properly handle boundary.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("someurl/{fileName}")
public Artifact uploadArtifact(
#FormDataParam("file") InputStream uploadedStream,
#FormDataParam("file") FormDataContentDisposition fileDetails,
#PathParam("fileName") String fileName) throws Exception;
I have developed a REST service using Jersey which looks like below
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(#FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("file") FormDataContentDisposition fileDetail, #FormDataParam("city") String city){
If I remove city String, then I am able to test file upload test in SoapUI by attaching the file with request.But, I am not able to test file upload along with String using SoapUI.