Can I have a rest service that can be used for file upload i.e. multi-part form data and JSON parameter? Below is the example of the service.
#POST
#Path("/upload")
#Consumes({ MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_JSON })
public Response uploadFile(#FormDataParam("file") InputStream uploadedInputStream,#FormDataParam("file") FormDataContentDisposition fileDetail, City city){
The problem is while testing I am trying to pass both file as an attachment and city object as JSON, it is giving me error as Content-Type could either be application/json or multipart/form-data.
Let me know if there is any way to handle this
You may Use Any Client Side Language to submit form with MultipartFile and Json data. I am writing Java Code in Spring MVC here. It will send String Json and MultiPartFile. then Me going to to Cast String JSON to Map, and Save File at Desired Location.
#RequestMapping(value="/hotel-save-update", method=RequestMethod.POST )
public #ResponseBody Map<String,Object> postFile(#RequestParam(value="file", required = false) MultipartFile file,
#RequestParam(value = "data") String object ){
Map<String,Object> map = new HashMap<String, Object>();
try {
ObjectMapper mapper = new ObjectMapper();
map = mapper.readValue(object, new TypeReference<Map<String, String>>(){});
}catch (Exception ex){
ex.printStackTrace();
}
String fileName = null;
if (file != null && !file.isEmpty()) {
try {
fileName = file.getOriginalFilename();
FileCopyUtils.copy(file.getBytes(), new FileOutputStream(servletContext.getRealPath("/resources/assets/images/hotelImages") + "/" + fileName));
} catch (Exception e) {
header.put(Utils.MESSAGE, "Image not uploaded! Exception occured!");
return result;
}
}
}
Can't you leave the #Consumes off and check the Content-Type header in the method itself, deciding what to do in code?
Your problem seems to be a restriction in the functionality of that annotation (is it Spring MVC?)
I have solved my problem by passing JSON as String from client and then converting String to JSON object.
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(#FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("file") FormDataContentDisposition fileDetail, #FormDataParam("city") String city){
Related
I'm creating a project in Java with Spring Boot.
The focus is to receive an image that is converted to a stream and that my code converts this image to a pdf file and sends this pdf back as a stream.
Despite the analysis, I can't get past the beginning, receiving the stream.. .
Here you'll see a snippet of my postman call to the running project
My Controller looks like this:
#RestController
public class Controller {
#PostMapping(value = "/convert/{format}", consumes = "application/octet-stream", produces = "application/octet-stream")
#ResponseBody
public void convert(RequestEntity<InputStream> entity, HttpServletResponse response, #PathVariable String format, #RequestParam Map<String, String> params) throws IOException {
if ("pdf".equalsIgnoreCase(format)) {
PDFConverter cnv = new PDFConverter();
/*cnv.convert(entity.getBody(), response.getOutputStream(), params);*/
response.setContentType("application/octet-stream");
response.getOutputStream().println("hello binary");
} else {
// handle other formats
throw new IllegalArgumentException("illegal format: " + format);
}
}
}
What do I overlook in this case?
I found the solution, in the controller I used RequestEntity<InputStream> entity, this gave the error. After changing this to HttpServletRequest request it worked.
#RestController
public class Controller {
#RequestMapping(value="/convert/{format}", method=RequestMethod.POST)
public #ResponseBody void convert(HttpServletRequest request, HttpServletResponse response, #PathVariable String format, #RequestParam Map<String, String> params) {
try{
if ("pdf".equalsIgnoreCase(format)) {
PDFConverter cnv = new PDFConverter();
response.setContentType("application/pdf");
cnv.convert(request.getInputStream(), response.getOutputStream(), params);
} else {
// handle other formats
throw new IllegalArgumentException("illegal format: " + format);
}
} catch (IllegalArgumentException | IOException e) {
e.printStackTrace();
}
}
}
As the error message tells you already, your content-type is not valid. You expecting a different content Type than you are sending off. Might be the problem that you append the charset definition to the request.
I think you are using commons-fileupload's streaming API. This won't work if spring.http.multipart.enabled=true, due to the request being pre-processed. Can you try setting spring.http.multipart.enabled=false and also change consumes = { MediaType.MULTIPART_FORM_DATA_VALUE },
What should be in the consumes and produces for Rest API which is returning byte[] of file in the response.
No file params are included in the request .
You could use the below for returning byte[]
#Produces(MediaType.APPLICATION_OCTET_STREAM)
You can use 'MultipartFile' for the purpose of consuming and sending back a file in response.
You can have a look at the following tutorial at spring.io for detailed tutorial:
https://spring.io/guides/gs/uploading-files/
Hope it helps!
You should set the media type on basis of file content type.
for example:
#GetMapping
public HttpEntity returnByteArray() {
String filepath = ; //filepath
String contentType = FileTypeMap.getDefaultFileTypeMap().getContentType(filePath);
byte[] byteContent = ; //Content
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf(contentType));
return new HttpEntity(byteContent, headers);
}
OR
If you always return the same content file type then you can also set in
#GetMapping(produces = "mime_type")
public byte[] returnByteArray() {
return new byte[0];
}
I create a function to download a CSV File. I will use that to download simple reports. I got the error below on Netbeans using Wildfly and JAX RS
RESTEASY002005: Failed executing POST /reports/downloadCSV/: org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response object of type: java.io.FileWriter of media type: application/octet-stream
Here is my Code:
Controller
Update on ParametersClass
#POST
#Path("/downloadCSV")
#Produces("application/octet-stream")
public Response downloadCSV(ParametersClass param) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
FileWriter fileWriter = null;
Date date = new Date();
try {
fileWriter = new FileWriter("MainReport_"+dateFormat.format(date)+".csv");
fileWriter.append(csvService.mainReport(dateFormat.parse(param.getStartDate()),dateFormat.parse(param.getEndDate())));
fileWriter.flush();
fileWriter.close();
ResponseBuilder response = Response.ok((Object) fileWriter);
response.header("Content-Disposition","attachment; filename=\"MainReport_"+dateFormat.format(date)+".csv\"");
return response.build();
} catch (ParseException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
The csvService returns a String like:
Column1,column2,column3
cellInfo1,cellInfo2,cellInfo3
,cellInfo2,cellInfo3
cellInfo1,,cellInfo3
cellInfo1,cellInfo2,
,,cellInfo3
I tried using a different #Produces => #Produces('text/csv') , #Produces('application/octet-stream')
If I remove the Annotation #Produces I got the following error:
RESTEASY002010: Failed to execute: javax.ws.rs.NotSupportedException: RESTEASY003200: Could not find message body reader for type: class com.xo.CSVFile of content type: application/x-www-form-urlencoded;charset=UTF-8
AJAX
var dateRange = new Object();
dateRange.startDate = '2017-07-20';
dateRange.endDate = '2017-08-10';
$.ajax({
type: 'POST',
url: appPath + '/api/reports/downloadCSV/',
data: JSON.stringify(dateRange),
async:true,
success: function(data) {
}
});
What I'm doing wrong ? Could you help to me please!
.
SOLUTION
Thanks to #albert-bos
1st. Check the link in the solution from #albert-bos below.
2nd: Check this link too
3rd:
Controller:
#POST
#Path("/downloadCSV")
#Produces("text/csv")
public List<LinkedHashMap<String, String>> downloadCSV(ParametersClass param) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
return csvService.mainReport(dateFormat.parse(param.getStartDate()),dateFormat.parse(param.getEndDate()));
} catch (ParseException ex) {
return null;
}
}
MessageBodyWriter:
I create a class called CSVMessageBodyWritter (check the link) but I adpated the method writeTo:
#Override
public void writeTo(Object t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
CsvSchema schema = null;
CsvSchema.Builder schemaBuilder = CsvSchema.builder();
if(t!=null){
List<LinkedHashMap<String, String>> reportArray = (List<LinkedHashMap<String, String>>) t;
LinkedHashMap<String, String> headers = reportArray.get(0);
for (String col : headers.keySet()) {
schemaBuilder.addColumn(col);
}
schema = schemaBuilder.build().withLineSeparator("\r");
CsvMapper mapper = new CsvMapper();
mapper.writer(schema).writeValues(entityStream).writeAll(reportArray);
}
}
JAX-RS only supports a few Content-Types by default (also depending on implementation), like XML and JSON.
The idea of JAX-RS is that it will convert an object to a certain type (e.g. XML or JSON). This is so you can re-use the same object for XML and JSON, without knowing the output in your Controller. Then If you want something different like CSV, you need to define your own BodyWriter, see example here: http://www.javaprocess.com/2015/08/a-simple-csv-messagebodywriter-for-jax.html
The problem here is that your controller is to specific for CSV and isn't very flexible. You could put your output of csvService into an object and let the BodyWriter convert it into CSV.
JS
window.open("http://localhost:8080/xmlcompare-rs/xmlcompare/excelmisreport");
Rest
#GET
#Path("excelmisreport")
#Produces("application/vnd.ms-excel")
public Response getExcelReport() {
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition",
"attachment; filename=MISReport.xls");
return response.build();
}
I don't have enough rep so can't add a comment for the answer of Albert Bos.
There is a PITFALL with that solution: you can suddenly run into the problem when you get empty csv file even if it should have a data. It happens because of result of getSize method.
I'm not sure which version of JAX-RS is supposed to be used in that example (in the article), but accordingly to the jax-rs documentation, result of getSize is ignored for JAX-RS 2.0, but for JAX-RS 1.X it seems to be used and "return 0" makes downloaded file empty. Make sure you return "-1". I've encountered it when tried to implement csv export in JIRA rest plugin (I guess it's based on first version of JAX-RS).
Hello i have the following jaxrs entry
#PUT()
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_OCTET_STREAM)
#ApiOperation(value = "Bla bla.")
#Path("secure/flappy")
public Response testput(
#ApiParam(value = "pwet",type = "file",format = "binary", required = true) InputStream certificate) throws Throwable {
try (InputStream stream = certificate) {
//Consume stream
return Response.ok().build();
}
}
And the generated swagger-ui page for this entry
I would like to know how to document my paremeter for getting only one parameter presented as a file chooser in swagger-ui.
#sdc: You're right i had to use multi part form data for getting a working file chooser in Swagger-ui. I also had to use #ApiImplicitParam for getting it working.
#PUT()
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.MULTIPART_FORM_DATA)
#ApiOperation(value = "Bla bla.")
#Path("secure/flappy")
#ApiImplicitParams({
#ApiImplicitParam(name = "file", value = "bla bla.", required = true, dataType = "java.io.File", paramType = "form")
})
public Response testput(#ApiParam(hidden = true) #FormDataParam("file") final InputStream certificate
) throws Throwable {
//TODO do something
return Response.ok().build();
}
I am not very familiar with Swagger UI, but this thread might be helpful
https://github.com/swagger-api/swagger-ui/issues/72
I see an example using an#ApiParam annotation
and this post talks about a file upload with the
def uploadFile annotation
https://github.com/swagger-api/swagger-ui/issues/169
#PUT
#Path("/secure/flappy")
#Consumes(Array(MediaType.MULTIPART_FORM_DATA))
#ApiOperation(value = "test a put file upload")
def uploadFile(
#ApiParam(value = "file to upload", required=false) #FormDataParam("file") inputStream: InputStream,
#ApiParam(value = "file detail") #FormDataParam("file") fileDetail: FormDataContentDisposition) = {
val uploadedFileLocation = "./" + fileDetail.getFileName
IOUtils.copy(inputStream, new FileOutputStream(uploadedFileLocation))
val msg = "File uploaded to " + uploadedFileLocation + ", " + (new java.io.File(uploadedFileLocation)).length + " bytes"
val output = new com.wordnik.swagger.sample.model.ApiResponse(200, msg)
Response.status(200).entity(output).build()
}
I suppose you need to use in the parameter description following:
#RequestPart("file") MultipartFile file
At least for me it gives a swagger form with a file selection button.
The solution was found in the examples here:
https://github.com/springfox/springfox-demos
I have a 3 step process to upload a file on a server:
Using EXT.JS form, I upload a file to a controler.
In the controler, the method gets the MultipartFile, builds a rest call using rest template and send it to the server.
On the server, the specified method should receive the multipart data and process the file.
Here is the method at the step 2 that get the file from the UI side (EXT.JS):
#RequestMapping("/customerUploadFile/upload")
#ResponseBody
public JsonResponse uploadFile(CustomerUploadBean bean,
#RequestParam("filePath") MultipartFile filePath) throws IOException {
long fileSize = filePath.getSize();
HttpEntity<CustomerUploadBean> httpEntity = getHttpEntity(bean);
byte[] byteArr = filePath.getBytes();
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("userId", httpEntity.getHeaders().get("userId"));
parameters.add("file", byteArr);
// to set content type of header
org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<JsonResponse> jsonResponse = restTemplate.exchange(uri, HttpMethod.POST,
new HttpEntity<MultiValueMap<String, Object>>(parameters, headers),
new ParameterizedTypeReference<JsonResponse>() {
});
JsonResponse response = jsonResponse.getBody();
if (jsonResponse.getStatusCode() == HttpStatus.OK) {
response.setSuccess(true);
} else {
response.setSuccess(false);
}
return response;
}
I've verified, the filePath object have the file and contains information about the file.
At the step 3, here is the method in the controller on the server that awaits for the file to process it:
public Response importUserProfileCSV(
#ApiParam(value = "Service Name", required = true) #PathParam("service") String service,
#ApiParam(value = "CSV file to upload.", required = true) #FormDataParam("file") InputStream uploadedInputStream,
#ApiParam(value = "CSV file detail", required = true) #FormDataParam("file") FormDataContentDisposition fileDetail) {
return delegate.importUserProfileCSV(uploadedInputStream, fileDetail,
service, "user");
}
The problem here is that at the step 3, in the method right above, the fileDetail object contains only null values... How can I adapt the method of step 2 (spring framework) to the method of step 3 (jersey).