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--
Related
I try to rebuild the server's response in order to store the received contents (Images and JSON).
The code of the server is like that of here
I changed the server to return TXT files, i get the folowing on the client.
Response response = client.post(body);
logger.info("content :" + response.readEntity(String.class));
--Boundary_9_2112219484_1499871974802
Content-Type: text/plain
Content-Disposition: attachement; filename="file1.txt"
Content of file 1
--Boundary_9_2112219484_1499871974802
Content-Type: text/plain
Content-Disposition: attachement; filename="file2.txt"
Content of file 2
--Boundary_9_2112219484_1499871974802--
Thank you for your help.
I would instead try to base 64 encode the image and then return it as a string in the JSON without any problems.
I am trying to upload a file to my Java JAX-RS(Jersey) web service.
My method signature on the server side if anyone interested is like this;
upload(#FormDataParam("file") InputStream uploadedStream, #FormDataParam("file") FormDataContentDisposition fileDetail, #FormDataParam("question") String req_question, #Context HttpHeaders headers)
When I try to upload a file with Postman (Client for testing) using multipart/form-data, it is working properly. When I sniff it with Fiddler I see this;
POST http://localhost:8080/application/question/post HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryT79BovKwL6ObBceu
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
------WebKitFormBoundaryT79BovKwL6ObBceu
Content-Disposition: form-data; name="file"; filename="PARS.png"
Content-Type: image/png
[HERE SOME STRANGE RAW BINARY DATA]
------WebKitFormBoundaryT79BovKwL6ObBceu
Content-Disposition: form-data; name="question"
{"title": "This is title!", "fk_field_id":1}
------WebKitFormBoundaryT79BovKwL6ObBceu--
As you can see it has proper differentation of types of data being sent. But when I try to achieve the same upload with Ionic/Angular my request look like this;
POST http://localhost:8080/application/question/post HTTP/1.1
Content-Type: multipart/form-data
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
/Exif II* [SOME STRANGE RAW BINARY DATA]
I only see binary, there is no differentiation nor sign of the params I sent. Here is my Angular code that is responded as failed.
let options: FileUploadOptions =
{
fileKey: 'file',
headers: headers,
params: {"title": "This is title!", "fk_field_id":1} // Actually I am getting this as method parameter yet to shorten code I moved it here
};
path = {MY ENDPOINT IN SERVER}
filePath = file:///storage/emulated/0/Android/data/io.ionic.starter/cache/1497313256081.jpg // Getting as method param
fileTransfer.upload(filePath, path, options).then(things => {
//Some console.log
});
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'm working on a REST resource that takes in a key and data (value) . I'd like to be robust so this data can be anything. It could range from a string to a file.
To handle this, I have the PUT REST method which is at the end of the post. Nothing fancy, just trying to PUT basic data. If I send data that is a string it works no problem.
However, if I try to send a file across, I get this error: org.jboss.resteasy.spi.UnsupportedMediaTypeException: Cannot consume content type. Here's why this doesn't make sense to me:
If I change the #Consumes from application/octet-stream to #Consumes("multipart/form-data"), I can send a file across just fine. When examining the file that's stored I see something like this:
------WebKitFormBoundaryfuQalizBHtg1BiLJ
Content-Disposition: form-data; name="fileUpload1"; filename="uploadedFile.extension"
Content-Type: application/octet-stream
/* file information here */
------WebKitFormBoundaryfuQalizBHtg1BiLJ--
Notice that the Content-Type is supposedly application/octet-stream which is what I tried consuming before but got the Cannot consume content type exception. I have no idea what would be causing this. I'm using the Advanced Rest Client extension for Chrome to send my request and it looks something like this: https://i.imgur.com/KvKCIkl.jpg
Any thoughts?
#PUT
#Path("{key}")
#Consumes("application/octet-stream")
public Response addData(
final #PathParam("key") String key,
final InputStream data) {
final Service service = new Service();
try {
service.addData(key, data);
} finally {
IOUtils.closeQuietly(data
}
return Response.status(204).build();
}
A multipart/form-data message contains a series of parts divided by a boundary. Every part has its own Content-Type. So in your example Content-Type: application/octet-stream is the Content-Type of one (and probably the only one) part but the Content-Type of the whole message is multipart/form-data. A full message with multiple parts could look like this:
Content-Type: multipart/form-data; boundary=WebKitFormBoundaryfuQalizBHtg1BiLJ
------WebKitFormBoundaryfuQalizBHtg1BiLJ
Content-Disposition: form-data; name="fileUpload1"; filename="uploadedFile.extension"
Content-Type: application/octet-stream
/* file information here */
------WebKitFormBoundaryfuQalizBHtg1BiLJ--
------WebKitFormBoundaryfuQalizBHtg1BiLJ
Content-Type: text/plain
/* some text here */
------WebKitFormBoundaryfuQalizBHtg1BiLJ--
I am trying to send a zipfile from my android application to our server and I keep getting a 411 length required error.
Here is the code that I am using to do that.
HttpPost post = new HttpPost("http://www.xyz.org/upload.json");
post.setHeader(C.constants.HTTP_CONTENT_TYPE, "application/octet-stream");
try {
FileInputStream fis = new FileInputStream("/data/data/org.myapp.appname/app_content.zip");
InputStreamEntity reqEntity = new InputStreamEntity(fis, -1);
post.setEntity(reqEntity);
String response = doPost(post);
Log.v(tag, "response from server " + response);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
What am I doing wrong here and may I also know how I can add more parameters with this post to send them to the server.
You should use a multipart MIME type, and parts for each parameters and for the files. See this old blog post of mine.
As you should know, an HTTP request (and response, too) is made up of two section: a header and a body (also called entity). In the header you specify a Content-Type so that the server script knows how to interpret the following bytes (you can also specify the encoding for textual data on the same line).
After the header, the client sends an empty line and an optional entity. A multipart entity is like a stack of parts separated by a boundary. This is what your desired HTTP request should look like on the wire:
POST /path/to/script.json HTTP/1.1
Host: localhost
Content-Type: multipart/form-data; boundary=a2ksdf0?--
a2ksdf0?--
Content-Disposition: form-data; name="profile_picture"; filename="me.png"
Content-Type: image/png
<content here>
a2ksdf0?--
Content-Disposition: form-data; name="username";
Content-Type: text/plain
Joe Black
BTW, it's the first time I see an upload script named upload.json. What do you use on the server side?