I am sending a file via Ajax like this:
// Get the selected files from the input.
var files = fileSelect.files;
// Create a new FormData object.
var formData = new FormData();
// Add the file to the request.
formData.append('photos[]', files[0], files[0].name);
$.ajax({
type:"POST",
url:"URL,
dataType:"json",
headers : {"cache-control":"no-cache"},
timeout : 12000,
processData: false,
data:{
formdata:formData
}
Now I want to work with the send file in my java class, in a ressource like this:
#PermitAll
#POST
#Path(URL)
#Produces(MediaType.APPLICATION_JSON)
public Map<String, Object> fileHandler(#FormParam("formdata") File formdata){ }
But accessing the file does not work, #FormParam("formdata") File formdata seems to be wrong (or more things?). I want to get access to this file in my ressource class somehow. What am I doing wrong? Maybe someone knows a better solution for this.
You can handle it this way:
I have changed the way FormData is passed. Used id of the form and passed it to create form data:
Javacript:
$.ajax({
type : 'POST',
url : rquestURL,
cache:false,
processData:false,
contentType:false,
data : new FormData($("#"+<your_form_id>)[0])}
Rresource (Added #Consumes(MediaType.MULTIPART_FORM_DATA) annotation):
#Path("/upload")
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
public ResponseDTO doUpload(FormDataMultiPart multiPartData) {
// non-file fields
final String imageId = multiPartData.getField("<your_field_name>").getValue();
// for file field
final FormDataBodyPart filePart = multiPartData.getField("<file_field_name>");
final ContentDisposition fileDetails = filePart.getContentDisposition();
final InputStream fileInputStream = filePart.getValueAs(InputStream.class);
// use the above fields as required
// file name can be accessed from field "fileDetails"
}
When you deal with files, it's not just FormParam, it's FormDataParam. Also, class File is for entity in your filesystem, not for files inside request. It should be InputStream instead.
So signature should look like this:
#PermitAll
#POST
#Path(URL)
#Produces(MediaType.APPLICATION_JSON)
public Map<String, Object> fileHandler(
#FormDataParam("formdata") InputStream formdata,
#FormDataParam("formdata") FormDataContentDisposition fileDetail
){ }
Here you could also see another parameter "FormDataContentDisposition", from it you could take details about data, like filename and size (would be useful, when you will read InputStream).
Note that I wrote this example for JAX-RS. Not sure what library you use.
Related
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 need to build a service that can receive 2 binary files (~100k each) and some metadata, preferably in json.
I found this, but it only seems to provide one InputStream to one of the parts. But I'd need two.. so what to do?
You have a few options
Simply add another parameter(s) with a different part annotation
#POST
#Consumes("multipart/form-data")
public Response post(#FormDataParam("file1") InputStream file1,
#FormDaraParam("file2") InputStream file2) {
}
The parts can have the same part name, so you could do
#POST
#Consumes("multipart/form-data")
public Response post(#FormDataParam("file") List<FormDataBodyPart> files) {
for (FormDataBodyPart file: files) {
FormDataContentDisposition fdcd = file.getFormDataContentDisposition();
String fileName = fdcd = getFileName();
InputStream is = file.getValueAs(InputStream.class);
}
}
You could traverse the entire multipart body youself
#POST
#Consumes("multipart/form-data")
public Response post(FormDataMultiPart mulitPart) {
Map<String, List<FormDataBodyPart>> fields = multiPart.getFields();
}
See Also:
Sending multiple files with Jersey: MessageBodyWriter not found for multipart/form-data, for a complete example
File upload along with other object in Jersey restful web service, for how the handle the JSON as a POJO.
Before I ask my question I have to say that I have read more than 20 questions and articles about this problem and none of them could solve it.
My problem is I have a restful server in java like this:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(#RequestBody final String stringRequest, final HttpServletResponse response)
{
try
{
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonRequest = objectMapper.readValue(stringRequest, JsonNode.class);
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
And I have tried different ways to send a json to this server with jquery (but all of them create errors):
$.ajax({
url:"/downloadByCode",
type:"POST",
data: JSON.stringify({"name":"value"}) });
415 "errors message" : "Content type 'application/x-www-form
urlencoded;charset=UTF-8' not supported", "type" :
"HttpMediaTypeNotSupportedError"
So I tried to fix it by adding contentType:
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
400 "errors message" : "Could not instantiate JAXBContext for class
[class java.lang.String]: null; nested exception is
javax.xml.bind.JAXBException\n - with linked
exception:\n[java.lang.NullPointerException", "type"
:"HttpMessageConversionError"
I tried to send json object directly instead of JSON.stringify and it gives the same 400 error.
I tried to add different consumes to the #requestMapping but still no luck.
I tried to define my own class instead of JsonNode but it does not change anything.
Any ideas?
Please try to create new class :
public class InputData{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
Then
public void downloadByCode(#RequestBody InputData stringRequest, final HttpServletResponse response)
And
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
try #RequestBody final Map<String, String> stringRequest
also you will need consumes = "application/json" on the #RequestMapping because you have that in your AJAX call
You will get 400 if spring doesn't like the format in which you send your ajax - I've had so much trouble with this in the past and it seems better to just ignore header types and content types unless necessary
You might try sending your response back as a ResponseEntity instead of using the HttpServletResponse directly. My hunch is that second argument, the HttpServletRequest argument, is what is causing the problem. I've never used that. I've always send my response back using the spring mvc api.
With Jersey api you can try just:
#POST
public void downloadByCode(String stringRequest)
and I think you'll find the body of your post in stringRequest.
You can take request body as string with usage of org.springframework.http.HttpEntity<String> as request type, here is example with your code as base:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(final HttpEntity<String> request, final HttpServletResponse response)
{
try
{
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonRequest = objectMapper.readValue(request.getBody(), JsonNode.class);
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
But maybe it will be better to use also String as return type, if you are planning to return result as string value, like this:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public String downloadByCode(HttpEntity<String> request) {
String requestBody = request.getBody();
String result;
// ...
// some processings here to create the result text
// ....
return result;
}
I made simple application using Spring Boot with usage of proposed solutions using HttpEntity and also additional example of usage POJO, to run application you need to have Maven and JDK >= 1.7.
#clonning repository with sample
git clone git#github.com:mind-blowing/samples.git
#change current folder to sample application root
cd samples/spring-boot/rest-handler-for-plain-text
#running application using maven spring-boot plugin
mvn spring-boot:run
After application will be started you can open http://localhost:8080 and you will see html page with simple usage of JQuery to send POST requests, text of request and response will visible on html page, in controller I added two handlers, first with usage of HttpEntity and second with usage of POJO.
Controller: SampleRestController.java
HTML page: index.html
Project: https://github.com/mind-blowing/samples/tree/master/spring-boot/rest-handler-for-plain-text
First of all If you are using maven you should add dependency for jackson
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
or you can download the jar and put it in our project class path (you can use other mapper as well)
then you should create a model or DTO class where you can map your json
public class Data{
private String name;
pubilc Data(){}
//getter and setter
}
THEN you controller
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public Data downloadByCode(#RequestBody final Data data, final HttpServletResponse response)
{
//your code
return data;
}
AJAX CALL
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
(Optional)You can override behavior by telling object mapper not to fail on missing properties by defining the bean as follows:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false));
return converter;
}
http://websystique.com/springmvc/spring-mvc-requestbody-responsebody-example/
Looking at your errors, it's clear that you have configured 'Jaxb2RootElementHttpMessageConverter' or similar XML converter in your spring configuration. And since you have registerned an XML converter, the #RequestBody and #ResponseBody work based on the registered message converters.
So, to solve your problem, go with a JSON message converter such as 'MappingJacksonHttpMessageConverter'. Once you register a JSON message converter, create a bean class to hold your json data and use it with RequestBody as below:
// It has to meet the json structure you are mapping it with
public class YourInputData {
//properties with getters and setters
}
Update 1:
Since you have defined multiple message converters, Spring tries to use the first one available by default. In order to use specific message converter(in this case Jackson converter), you should specify 'Accept' header from client like below:
$.ajax({
headers: {
"Accept" : "application/json; charset=utf-8",
"Content-Type": "application/json; charset=utf-8"
}
data: "data",
success : function(response) {
...
} })
The final answer is a combination of a number of answers/comments in this question that I am going to summarize them here:
1- You have to make sure you have an appropriate json converter in your spring config such as MappingJacksonHttpMessageConverter (credits to #java Anto)
2- You have to create a POJO class with same structure as your json object (see #Vinh Vo answer)
3- Your POJO class cannot be an inline class unless it is a static class. It means it should have its own java file or it should be static. (credits to #NTyler)
4- Your POJO class can miss parts of your json object if you set it appropriately in your object mapper (see #Aman Tuladhar answer)
5- Your ajax call requires contentType:"application/json", and you should send your data with JSON.stringify
Here is the Final code that is working perfectly:
public static class InputData
{
private String name
public String getName()
{
return name;
}
public void setName(final String name
{
this.name = name;
}
}
#RequestMapping(value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(#RequestBody final InputData request, final HttpServletResponse response)
{
try
{
String codes = request.getName();
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
And it is the jquery Ajax request:
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
Delete the #ResponseBody on your downloadByCode method
Change your method downloadByCode() return type to String and then return the String
Response body will automatically convert the returned String to JSON and then use the data appropriately
I am not that well versed with java but as much as I know your java code must be something like this.
public class downloadByCode{
#GET
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response downloadByCode(#QueryParam("paramater1") final String parameter 1, #Context HttpServletRequest httpRequest) {
If this not helps you can keep you code somewhere and share it.
This is my web service's declaration
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("application/json")
public DeviceDbUploadResponse upload(#FormDataParam("file1") InputStream file1,
#FormDataParam("file2") InputStream file2,
#FormDataParam("name1") String filename1,
#FormDataParam("name2") String filename2,
#FormDataParam("ID") String ID)
My web service call
var fd=new FormData();
fd.append("ID",ID);
/* lines of code here */
$.ajax({
url: 'http://localhost:8080/linterm2m/webapi/m2m/upload',
data: fd,
processData: false,
contentType: false,
type: 'POST'
});
Everything works well so far. Now it is required to receive all the data (filename and ID) through an Request Object, something like:
public class Request{
String ID;
String filename1;
String filename2;
}
But I doubt it can be fulfilled because of the multipart-form-data consuming type. I need some enlightenment and a solution.
Composing multipart/form-data with a different Content-Type on each parts with Javascript (or Angular)
I try following the answer in this question and it works.
fd.append('whole', new Blob([JSON.stringify({
ID: ID,
name1:file1.name,
name2:file2.name,
})], {
type: "application/json"
}));
The Request class is just as I mentioned.
The name1,name2 part is not needed but I just want to test with an Object with various attributes.
Much appreciation for Naman's help about FormDataContentDisposition.
I am having a question that how can I send JSON Object from Springs MVC so that I can convert it into a JavaScript Object on my HTML Page.
In a traditional way I do it: Below is a snippet from Java Servlet which sets a request attribute and forward it to the JSP Page.
JSONObject jsonObj = new JSONObject();
jsonObj.put("name","test");
jsonObj.put("age",24);
request.setAttribute("jsonObj",jsonObj);
request.getRequestDispatcher("test.jsp").forward(request,response);
In JSP I retrieve as :
<script type="text/javascript">
var jsonObj =<%=request.getAttribute("jsonObj"); %>;
alert("name = "+jsonObj.name+" ; age = "+jsonObj.age); // This will output name and age from the JSON Object
</script>
So what I need to ask how can I do the same in Springs MVC. How can I send the JSONObject from Dispatcher Servlet, and convert it to JS object in my JSP page ?
An easy way to do this using the ObjectMapper. It will create an JSON String from your Object. And that you can send to your view/jsp.
I put an small example of a controller I do this (just snipped).
#Controller
public class UsersSettingsController {
#Autowired
UserSettingsDefaultService userSettingsService;
#RequestMapping(value="/userSettings/dynamic/userSettings", method=RequestMethod.GET)
public ModelAndView get() throws Exception {
ModelAndView mav = new ModelAndView();
ObjectMapper mapper = new ObjectMapper();
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserSettings userSet = userSettingsService.getUserSettingsByUser(user);
mav.addObject("userSettingsJSON", mapper.writeValueAsString(userSet));
mav.setViewName("userSettings/dynamic/filter");
return mav;
}
}
Or course can you create your JSON Object in your Contoller step by step like you did in your example. Then you don't need the Mapper, just sending the String to your View.
In the JSP you load the json String like this into a JS var:
var jsonString = '${userSettingsJSON}';
To get elements from JSON String or parse, see: http://www.json.org/js.html.
I'm an KnockOut Framework Fan would do it with it.
I think you should use ajax(for example jquery),the following is spring mvc
#RequestMapping(value = "/admin/new/list", method = RequestMethod.GET)
#ResponseBody
public List<News> list()
{
return newsDao.findAll();
}
and in the jsp page,you may use ajax util (for example jquery)
$.ajax({
type: "GET",
url: '<c:url value="/admin/new/list"/>',
cache:false,
dataType :'json',
success: function(data){
alert(data);
}
});
the data is json object
I don't know whether the above is what you need
A much easier way to do this is to include the Jackson dependencies in maven and use #ResponseBody to return a JSON representation of the object, without having to manually write the manipulation.
Have a look at the example below, you shouldn't have to write any tranlation to JSON code.
http://www.mkyong.com/spring-mvc/spring-3-mvc-and-json-example/
As per your requirement, I suggest you to use AJAX call with JSON as data type.
For example :
$.ajax({
url: "getFormatIdDescMap?compId="+compId,
global: false,
type: "POST",
dataType:"json",
contanttype:'text/json',
async:false,
error:function(){
alert("Error during retrieving the foramt ID List");
},
success: function(data){
//REMOVE ALL THE OLD VALUES OF FORMAT DROP DOWN
removeDropDownVals('formatList');
var optn;
for(var index=0;index<data.formatKeys.length;index++){
optn = document.createElement("option");
document.getElementById("formatList").options.add(optn);
optn.text =data.formatDescs[index];
optn.value=data.formatKeys[index];
}
}
});
});
In the code above, I am preparing a new list of Formats based on company ID. You can iterate over the response.
It will give response text as per your requirements. But here note that .. if you are getting json Array in the response, it will contain that data in square bracket like..[1,2,3] and If you are getting response in JSON Object then it will be displayed in curly braces like {"data",[1,2,3]}.