I have a service where I want to be able to optionally upload a file (including a file will run a separate function) with a POST request.
A simplified version of what my ReqestMapping looks like is this:
#ApiOperation(value = "Data", nickname = "Create a new data object")
#RequestMapping(value = "/add/{user_id}", produces = "application/json", method = RequestMethod.POST)
public ResponseEntity<Data> addData(#RequestParam("note") String body,
#RequestParam("location") String location,
#RequestParam(value = "file", required = false) List<MultipartFile> file,
#PathVariable String user_id){
if (file != null) {
doSomething(file);
}
doRegularStuff(body, location, user_id);
return new ResponseEntity(HttpStatus.OK);
}
As can be seen, I have the required = false option for my List of multipart files. However, when I attempt to curl the endpoint without any files and while stating that my content type is Content-Type: application/json, I get the error that my request isn't a multipart request.
Fine. So I change to Content-Type: multipart/form-data and without any files, I get the request was rejected because no multipart boundary was found (obviously, since I don't have a file).
This leads me to wonder how I can have a optional multipart parameter in my Spring endpoints? I would like to avoid having to add additional parameters to my request, such as "File Attached: True/False" as that can become cumbersome and unnecessary when the server can just check for existence.
Thanks!
There is no problem in your code, but the problem in client request, because Content-Type should be like below if you want to upload image,
multipart/form-data; boundary="123123"
try to remove the Content-Type header and test, i will put one example for server code and client request
Server code:
#RequestMapping(method = RequestMethod.POST, value = "/users/profile")
public ResponseEntity<?> handleFileUpload(#RequestParam("name") String name,
#RequestParam(name="file", required=false) MultipartFile file) {
log.info(" name : {}", name);
if(file!=null)
{
log.info("image : {}", file.getOriginalFilename());
log.info("image content type : {}", file.getContentType());
}
return new ResponseEntity<String>("Uploaded",HttpStatus.OK);
}
Client Request using Postman
with image
without image
Curl example:
without image, with Content-Type
curl -X POST -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "name=test" "http://localhost:8080/api/users/profile"
without image, without Content-Type
curl -X POST -F "name=test" "http://localhost:8080/api/users/profile"
Related
my Spring Boot Resource is not receiving my requests.
this is the resource definition:
#PostMapping(name = "sign_one_doc", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<InputStreamResource> signDocument(
#RequestParam("file") MultipartFile data,
#RequestPart("isTimestamping") boolean isTimestamping,
#RequestPart("isMakeCheck") boolean isMakeCheck,
#RequestPart("signMode") int signMode,
#RequestPart("certClientID") int certClientID,
#RequestPart("isCertLocal") boolean isCertLocal
) throws IOException, DocSignException, InputStreamReadException {
RequestData requestData
= signCtrl.signFile(data.getInputStream(), data.getOriginalFilename(), signMode, certClientID, isTimestamping, isMakeCheck, isCertLocal);
HttpHeaders headers = new HttpHeaders();
InputStreamResource inputStreamResource = new InputStreamResource(requestData.currentCtxFile());
headers.setContentLength(requestData.currentCtxFileBA().length);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<InputStreamResource>(inputStreamResource, headers, HttpStatus.OK);
}
request that I'm sending (via Insomnia) has content-type: multipart/form-data and accept: application/octet-stream set
but when I send this requeste spring just says:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported]
Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
what am I setting wrong and why? also, would you be so kind and provide curl request for this resource?
I have tried:
curl -v --include -F isTimestamping=false -F file=#test.pdf -H 'Content-Type: multipart/form-data' http://localhost:8080/sign_one_doc
but spring fails with:
java.io.EOFException: Unexpected EOF read on the socket
man, it's hard to sent a request, everything that's possible just fails :D
I need to change #RequestPart to #RequestParam.
curl issue still unresolved
I want to create a API which can have parameter as multipart file and JSON object (#RequestBody). Please find following snippet while calling this API. I am getting HTTP 415 Unsupported Media Type error. If I remove #RequestBody LabPatientInfo reportData then it works fine.
#RequestMapping(value={"/lab/saveReport"}, method={RequestMethod.POST},
consumes={"multipart/form-data"}, headers={"Accept=application/json"})
#ResponseBody
public ResponseEntity<String>
saveReport(#RequestParam(value="reportFile") MultipartFile reportFile,
#RequestBody LabPatientInfo reportData) throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
logger.info("in Lab Save Report");
logger.info("Report Data {} ", reportData);
//logger.info("Request BODY {} ", request.getAttribute("data"));
return new ResponseEntity<String>(HttpStatus.OK);
}
following is LabPatientInfo class.
#RooJson(deepSerialize = true)
#RooToString
public class LabPatientInfo {
private String firstName;
private String phoneNumber;
private String DateOfBirth;
private Integer age;
private String gender;
private String refferedBy;
private String reportfile;
private String reportType;
private String reportDate;
private String purpose;
private String followUpDate;
private List<ReportDataInfo> analytes;
while hitting API I am passing following JSON object with uploaded file..
{
"firstName":"abc",
"phoneNumber":"898989",
"DateOfBirth":"asas",
"age":"asas",
"gender":"asas",
"refferedBy":"asas",
"reportfile":"asas",
"reportType":"asas",
"reportDate":"asas",
"purpose":"asas",
"followUpDate":"asas",
"analytes":null
}
You can use #RequestPart like below. This will support both json object and multipart file.
#ResponseBody
public ResponseEntity<String>
saveReport(#RequestPart (value="reportFile") MultipartFile reportFile,
#RequestPart LabPatientInfo reportData) throws IOException {
In order to test it using curl you can create one file for your json part (reportData). Say for example you create "mydata.json" file and paste your json payload in it. And say your reportFile is "report.txt". Now you can send request using curl like below.
curl -v -H "Content-Type:multipart/form-data" -F "reportData=#mydata.json;type=application/json" -F "reportFile=#report.txt;type=text/plain" http://localhost:8080/MyApp/lab/saveReport
An example of a post method which receives a json object and a generic file:
public ResponseEntity<Resource> postGenerateReport(#RequestPart GenerateReportDTO, generateReportDTO, #RequestPart MultipartFile jxhtmlReport)
For the PostMan setup (or curl or anyother REST test utility) you just have to add form-data request with 2 elements:
Key:generateReportDTO, Value: File with .json extension (and compatible content with the object)
Key:jxhtmlReport, Value: just any file.
Gl
When a parameter is annotated with #RequestPart the content of the part is passed through an HttpMessageConverter to resolve the method argument with the 'Content-Type' of the request part in mind. This is analogous to what #RequestBody does to resolve an argument based on the content of a regular request.
so, we can parse #Requestbody as #RequestPart as "abaghel" and reportData need to be a json file.
Spring Roo 2.0.0.M3 includes support for automatic scaffolding of a REST API.
For complete information, see the REST API in the reference manual.
Note the M3 version generate artifacts that could change in newer versions, so your project might not upgrade automatically if you open it with RC1 or above.
May the Force be with you.
I have some xml files stored as strings in my database and scala+spring based backend with this controller:
#RequestMapping(value = Array("/download"), method = Array(RequestMethod.GET))
def downloadFile(#RequestParam filename: String, //some more params
response: HttpServletResponse) = {
val fileContent = // some logic here, returns file content as String
response.setContentType("application/xml")
response.setHeader("Content-Disposition", s"attachment;filename=$filename")
response.setStatus(HttpServletResponse.SC_OK)
response.getOutputStream.write(fileContent.getBytes)
response.flushBuffer()
}
Also i have this script:
$.ajax({
type: "GET",
url: url,
data: {
filename: filename //and some more params
}
})
Then i send HTTP request to server, get right HTTP response and then nothing happens. All info i have from browser logs is that response has file content in body and headers, but download never starts.
What am i doing wrong?
I saw these SO Q&A but they didnt help me at all:
download file using an ajax request
Downloading a file from spring controllers
UPD1:
Also tried this one, with no result.
This is how I made a file to download from a server.
output = (byte[]) processedDocumentObject;
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
responseHeaders.setContentDispositionFormData("attachment", "file.xml");
HttpEntity<byte[]> fileEntity = new HttpEntity<byte[]>(output,responseHeaders);
return fileEntity;
However this is in java and HttpHeaders is org.springframework.http.HttpHeaders and HttpEntity is org.springframework.http.HttpEntity<byte[]>
Also, you need to convert your string into byte array initially.
I am trying to send a JSON string as a request to my application. This is my code:
#RequestMapping(
value = "/mylink/upload",
method = RequestMethod.POST,
consumes ="application/json",
produces = "application/json")
public
#ResponseBody
List<Upload> upload(
#RequestParam(value = "hdfsLocation") String hdfsLocation
) throws Exception {
return S3HdfsTransfer.uploadFromHDFS(hdfsLocation);
}
I am trying to send a request with Postman. The method I use is POST, the header contains: Accept "application/json",Content-Type "application/json", the request body is the following:
{
"hdfsLocation" : "hdfs://145.160.10.10:8020"
}
This is the response I get. If I put the parameter in the URL, it works.
{
"httpStatus": 500,
"appErrorId": 0,
"message": "Required String parameter 'hdfsLocation' is not present",
"trackingId": "8c6d45fd-2da5-47ea-a213-3d4ea5764681"
}
Any idea what I am doing wrong?
Thanks,
Serban
Looks like you have confused #RequestBody with #RequestParam. Do either of following :
Pass the request param as a request param(not as a body). Like, (encoded)
http://example.com?hdfsLocation=http%3A%2F%2Fexample.com%3FhdfsLocation%3Dhdfs%3A%2F%2F145.160.10.10%3A8020
Replace the #RequestParam with #RequestBody. If you are sending a body, don't send it along with request param. Those are two different things.
I guess you over looked :)
Shouldn't it be #RequestBody instead of #RequestParam?
Also, even after using #RequestBody, the whole of the JSON string:
{
"hdfsLocation" : "hdfs://145.160.10.10:8020"
}
will be the value of String hdfsLocation and not just the hdfs url. Hence, you'll have to JSON parse that JSON by yourself to get just the hdfs url.
Why doesn't this work? I am trying this against my Spring 3.2 MVC application. It works with POST requests.
curl -X PUT http://localhost:8080/bananas/1 --form data='{"description":"Hei", "status":"REJECTED"};type=application/json'
java.lang.IllegalArgumentException: Failed to obtain request part:
data. The part is missing or multipart processing is not configured.
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity updateQuestion(#PathVariable final int id,
#RequestPart(value = "data") final Banana banana,
#RequestPart(value = "image", required = false) final MultipartFile imageFile) {
In reality PUT requests are supposed to work on Request Body (or payload) and not on Request Parameters. In that sense, servlet API & spring's handling is correct.
A better easier way to pass no data element from your jquery and javascript call.Pass your parameter as part of url.Means Set parameter in the url field.otherwise u have to do your code using GET.
var x=10;
$.ajax({
url: "/ritesh/" + x + "/patil" + "?param1=param2Val&..",
type: "PUT",
data: "",
success: function(response) {
// ....
}
});