Download file with ajax() POST request via Spring MVC - javascript

I try to download a file. The action is triggered by ajax() POST request. The request sends data in JSON format to the controller. The controller generates the file (bytes) and sends it back.
JavaScript:
function getLicenseFile() {
$.ajax({
type: 'POST',
url: '<%=request.getContextPath()%>/licenses/rest/downloadLicenseFile',
dataType: 'json',
contentType: 'application/json;charset=UTF-8',
data: ko.mapping.toJSON(licenseModel),
success: function (data) {
console.log("in sucess")
},
error:function (xhr, ajaxOptions, thrownError){
console.log("in error")
}
});
}
Controller:
#RequestMapping(value = "/licenses/rest/downloadLicenseFile", method = RequestMethod.POST)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody
public void createLicenseFile(#Valid #RequestBody License license, HttpServletResponse response) throws Exception {
logger.debug("Contoller License in: "+ license);
byte[] licensedata = licenseEncodeDefaultService.createLicenseFile(license);
logger.debug("licenseData: " + new String(licensedata));
response.setHeader("Content-Disposition", "attachment; filename=\"" + license.getCustomer() + ".license\"");
response.getOutputStream().write(licensedata);
response.flushBuffer();
}
Problem
The Browser should open a download box, but it does not happen
The response is handled in the error: section of ajax function (but the HTTP Status is OK)
So what do I do wrong or what is the proper way to do this?

Just send a URL of file in response and then "visit" it in your success callback.
function getLicenseFile() {
$.ajax({
type: 'POST',
url: '<%=request.getContextPath()%>/licenses/rest/downloadLicenseFile',
dataType: 'json',
contentType: 'application/json;charset=UTF-8',
data: ko.mapping.toJSON(licenseModel),
success: function (data) {
window.open(data.fileUrl);
// or window.location.href = data.fileUrl;
},
error:function (xhr, ajaxOptions, thrownError) {
console.log("in error");
}
});
}
data.fileUrl should be set in response by server to say client where to get the file.
So your server will send a response with JSON like
{
"fileUrl": "http://mysite.com/files/0123456789"
}

#will824 As you ask I'll post my own solution.
I used a workaround in controller and save the file temporarily in the files ystem (/tmp). I split up the function in 2 steps. Creating and downloading.
This is not very nice but good enough for me.
Controller (creates a file, will be saved on the server file system):
#RequestMapping(value = "/licenses/rest", method = RequestMethod.PUT)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody
public String createLicenseFile(#Valid #RequestBody License license) throws Exception {
// create encrypted license file and send the name back to view
String fileName = licenseEncodeDefaultService.createLicenseFile(license);
return fileName;
}
Controller (downloads a file):
#RequestMapping(value = "/licenses/downloadFile/{file}", method = RequestMethod.GET)
public void downloadLicenseFile(#PathVariable("file") String file, HttpServletResponse response) throws Exception {
// create full filename and get input stream
File licenseFile = new File ("/tmp/" + file);
InputStream is = new FileInputStream(licenseFile);
// set file as attached data and copy file data to response output stream
response.setHeader("Content-Disposition", "attachment; filename=\"" + file + ".license\"");
FileCopyUtils.copy(is, response.getOutputStream());
// delete file on server file system
licenseFile.delete();
// close stream and return to view
response.flushBuffer();
}
JavaScript:
function getLicenseFile() {
//console.log(ko.mapping.toJSON(licenseModel));
$.ajax({
type : 'PUT',
url : '${pageContext.request.contextPath}/licenses/rest',
dataType : 'text',
contentType : 'application/json;charset=UTF-8',
data : ko.mapping.toJSON(licenseModel),
success : function(data) {
window.location.href = '${pageContext.request.contextPath}/licenses/downloadFile/'
+ data;
},
error : function(xhr, ajaxOptions, thrownError) {
// error handling
}
});
}

If you want download file without change URL, you can call form.submit() programmatically instead of using AJAX.
JavaScript:
function downloadFileUsingForm(url) {
var form = document.createElement("form");
form.method = "post";
form.action = url;
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
downloadFileUsingForm("/YourController/DownloadFile");
Controller:
[HttpPost]
public ActionResult DownloadFile()
{
string content = "Some Values";
byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes(content);
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "file.txt");
}

As the comments said you can't do it with an ajax call, but you can do it with plain Javascript.
function getLicenseFile() {
var downloadUrl = "${pageContext.request.contextPath}/licenses/rest/downloadLicenseFile";
// (optionally) provide the user with a message that the download is starting
window.location.href = downloadUrl;
}
Note the use of ${pageContext.request.contextPath}, which is preferred over <%=request.getContextPath()%>.

Ajax is not going to help you try with hidden form approach
<form action='../servletname' method='POST' id='formid'>
<input type='hidden' value='' name='name' id='id'/>
<input type='hidden' value=' ' name='name' id='id' />
</form>
pass you json through form field
on click of of your download button submit form
$('#formid').submit();
then in server side
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=filnemae.fileformat");
ServletOutputStream out = res.getOutputStream();
write on ouput stream then close or flush
if you are sending large data through post update postsize in server.xml

Related

Ajax POST FormData throw exception in Spring MVC

Yesterday I had a similar problem where Ajax POST method returned 405 error which was caused by csrf token. Someone helped mi with this, but now I'm powerless what is happening.
I have an Ajax POST request:
$(document).ready(function(){
var mic, recorder, soundFile;
setup();
})
function setup() {
mic = new p5.AudioIn();
mic.start();
recorder = new p5.SoundRecorder();
recorder.setInput(mic);
soundFile = new p5.SoundFile();
}
function toggleRecording(e) {
if (e.classList.contains("recording")) {
recorder.stop();
e.classList.remove("recording");
sendAudioToServer(soundFile)
} else {
e.classList.add("recording");
recorder.record(soundFile);
}
}
function sendAudioToServer(soundFile)
{
var data = new FormData();
data.append('file', soundFile);
$.ajax({
method: 'POST',
enctype: 'multipart/form-data',
url: '/recognizeCommand',
data: data,
processData: false,
contentType: false,
success: function(data) {
alert("works!");
},
error: function(xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
})
}
soundFile is an object from p5.js library which contain audio. I also try with simple String but there is the same error
And a controller in Spring MVC:
#RequestMapping(value = "/recognizeCommand", method = RequestMethod.POST)
public #ResponseBody String recognizeCommand(#RequestParam("file") MultipartFile multipartFile) {
try {
SpeechRecognitionApplication.logger.info("BEFORE: " + multipartFile);
byte[] bytes = multipartFile.getBytes();
SpeechRecognitionApplication.logger.info(bytes);
} catch (IOException e) {
e.printStackTrace();
}
return "finish";
}
When I send this Ajax request it throws error 400 and there is exception in Spring:
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:199) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:112) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) [spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131) [spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877) [spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
What is important that when I tested endpoint with postman and send some file as parameter it works correctly.
I know there are similar posts on this forum but seriously I checked each of them, try every solution and nothing can help.
I believe that some of you will have any idea how to solve this problem.
EDIT
Added this 3 lines before ajax post method:
data.append('file', "example");
console.log("file: " + data.get("file"));
console.log(data);
returns:
I guess problem is not with spring controller, but the way the file is passed to request. To ensure that file is passed, you can log into browser to check if file is present :
console.log("file" + data.get("file"));
Could you show the code you use to get file from input ?
EDIT:
Could you tes your endpoint with this simle file upload form ?
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
function handleFileSelect() {
var data = new FormData();
input = document.getElementById('fileinput');
data.append('file', input.files[0]);
console.log("file" + data.get("file"));
$.ajax({
method: 'POST',
enctype: 'multipart/form-data',
url: 'http://localhost:8080/test/recognizeCommand',
data: data,
processData: false,
contentType: false,
success: function (data) {
alert("works!");
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
}
</script>
</head>
<body>
<input type="file" id="fileinput"/>
<input type='button' id='btnLoad' value='Test' onclick='handleFileSelect();'>
</body>
</html>
You are getting this exception
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
and if you look this part of yor code
public #ResponseBody String recognizeCommand(#RequestParam("file") MultipartFile multipartFile)
You are using #RequestParam, so you have to change your code to
#RequestMapping(value = "/recognizeCommand", method = RequestMethod.POST, consumes = { "multipart/form-data" })
public #ResponseBody String recognizeCommand(#RequestPart("file") MultipartFile multipartFile) {
and it should work

Upload a simple file and input to Java

I'm using Spring MVC. I want to upload a file but my attempts are not working. This is my last attempt.
I debugged and I correctly get the file object and the form serialized in ajax.
I'm getting this so obviously I'm doing something bad.
POST http://localhost:8080/upload/testCase 400 (Bad Request)
How do I get the form data and file on the Controller?
HTML:
<input id="testName" class="form-control" name="testName" placeholder="Insert a name for the test case">
<input type="file" name="fileUpload" id="fileUpload" class="hide"/>
Javascript
$(document).ready(function () {
var file = new FormData();
$('#fileUpload').change(function () {
file.append('file',$('#fileUpload')[0].files[0]);
console.log($('#fileUpload')[0].files[0]);
});
$('#btnSubmit').click(function (e) {
e.preventDefault();
var data = $('#creationForm').serialize();
console.log(data + file);
$.ajax({
type: 'post',
url: '/upload/testCase',
data: {data: data, file: file},
processData: false,
success: function (result) {
},
error: function () {
}
});
return false;
});
});
Controller Java
#RequestMapping(value = "/upload/testCase" , method = RequestMethod.POST)
public #ResponseBody String uploadTestCase(#RequestParam("data") String data, #RequestParam("file") File file ) {
//TestCases.upload(file);
System.out.println(data + file);
return "";
}
BackEnd
public static void upload(MultipartHttpServletRequest request) {
//1. get the files from the request object
Iterator<String> itr = request.getFileNames();
MultipartFile mpf = request.getFile(itr.next());
System.out.println(mpf.getOriginalFilename() +" uploaded!");
}

how to download file into aclient from server without Returning filepathresult

i saw this code for downloading a file into client from server...
public ActionResult Download()
{
string file = #"c:\someFolder\foo.xlsx";
string contentType = "application/pdf";
return File(file, controntType, Path.GetFileName(file));
}
But the problem is that i have to do two different things in this Download method.Atfirst ,i want to download the file and then i need to get path of downloaded file and pass in to a documentviewr to display it.so i wrote this code in view razor.
function navigate(target) {
$.ajax({
url: '#Url.Action("Download", "Patient")',
type: 'GET',
dataType: 'json',
cache: false,
data: { 'filepath' : target },
success: function (results) {
event.preventDefault();
documentViewr.loadDocument(results);
},
error: function () {
alert('Error occured');
}
});
}
and in my controller i wrote this:
[HttpGet]
public JsonResult Download(string filepath)
{
var content_type = "";
//path = Path.Combine("D:\file1", filename);
content_type = "application/pdf";
FilePathResult file = File(filepath, content_type,
Path.GetFileName(filepath));
String sDocID, sDocPath;
ViewerController vc = new ViewerController();
sDocPath = Server.MapPath(".") + "\\App_Data\\sample.docx";
sDocID = vc.LoadDocument(sDocPath);
return Json(sDocID, JsonRequestBehavior.AllowGet);
}
so my question is that how i can merge this two actions together?because if i do not return FilePathResult i can not download the file..my another question is that how i can define the path in which file will be downloded?can i tell it to download files into for example Server.MapPath(".") + "\App_Data\...??
i will be grateful if u can help me....

how to reply to ajax post using servlet [duplicate]

This question already has answers here:
How should I use servlets and Ajax?
(7 answers)
Closed 6 years ago.
i have a username textbox that when a user input a name it takes that name and send to server and the server check if the username has not been taken by anybody else, if so i need to send back somedata and tell the client that the username has been taken else if it is not i need to turn the textbox border color to green, now i can do sending username value to server but i don't know how to send back and how to receive the sent data using jquery ajax.
here is my code:
client:
$(document).ready(function() {
$('#Registeration_Username_box').on('input', function() {
postUsernameToServer();
});
function postUsernameToServer() {
var formData = {
'username': $('input[name=UserName]').val(),
};
// process the form
$.ajax({
type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
url: '../dusernameavailable', // the url where we want to POST
data: formData, // our data object
dataType: 'json', // what type of data do we expect back from the server
encode: true
}).done(function(data) {
console.log(data);
});
}
});
servlet:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
String str = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
System.out.println(str);
}
Add success and error to your ajax
function postUsernameToServer() {
var formData = {
'username': $('input[name=UserName]').val(),
};
// process the form
$.ajax({
type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
url: '../dusernameavailable', // the url where we want to POST
data: formData, // our data object
dataType: 'json', // what type of data do we expect back from the server
encode: true,
success: function(data) {
//TODO make the box green or whatever your requirement is
},
error: function() {
//TODO username already taken
}
});
}
});
In servlet send appropriate response.
You will need to send a response code other than 200 for ajax to consider it as an error from the server.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String str = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
System.out.println(str);
//TODO - check if user name exists or not
if(user name already exists){
response.setProperty(response.HTTP_STATUS_CODE, "500");
PrintWriter out = response.getWriter();
out.println("error");
out.close();
}
else{
PrintWriter out = response.getWriter();
out.println("success");
out.close();
}
}
}
Add a success handler to your ajax call.
You can try something like below; where 'green' and 'red' are css classes you defined in your CSS file .
$.ajax({
type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
url: '../dusernameavailable', // the url where we want to POST
data: formData, // our data object
dataType: 'json', // what type of data do we expect back from the server
encode: true,
success: function(response) {
if(response.isUserNameAvailable == true)
$('input[name=UserName]').addClass('green');
else
$('input[name=UserName]').addClass('red');
}
}).done(function(data) {
console.log(data);
});

JQuery/Ajax & Spring Rest Multi-part form submit

I am quite new to JQuery and I was trying to do some asynchronous multipart form uploading. The form consist of few data fields and a file type. I have set up the server side code (Spring) like this:
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody
Upload multipleSave(MultipartHttpServletRequest request)
{
Upload upload = new Upload();
Iterator<String> iterator = request.getFileNames();
while (iterator.hasNext())
{
MultipartFile file = request.getFile(iterator.next());
try
{
System.out.println(MessageFormat.format("File Length: {0}", Arrays.toString(file.getBytes())));
System.out.println("File Type: " + file.getContentType());
upload.setContent(file.getBytes());
upload.setDocId(id++);
upload.setError(null);
upload.setName(file.getName());
upload.setSize(file.getSize());
fileList.put(upload.getDocId().toString(), upload);
} catch (Exception e)
{
System.out.println("Error occurred: " + e);
upload.setError("500: Something went wrong!");
}
}
return upload;
}
and client side code like this:
function processFileUpload()
{
console.log("fileupload clicked");
var formData = new FormData();
formData.append("file", files[0]);
$.ajax({dataType: 'json',
url: "/SpringJqueryFileUpload/upload",
data: formData,
type: "POST",
enctype: 'multipart/form-data',
processData: false,
contentType: false,
success: function (result) {
alert('success' + JSON.stringify(result));
},
error: function (result) {
alert('error' + JSON.stringify(result));
}
});
}
When I do submit, the server responds with this:
java.lang.IllegalArgumentException: No converter found for return value of type: class com.upload.model.Upload
I am wondering with error. Could I be missing something here??
I would try changing your annotation to:
#RequestMapping(method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
And make sure you have Jackson (which Spring uses for JSON serialization) properly on your path. Also, make sure your Upload class is serializable, e.g. is not private or anything like that. If it is just a normal Java bean type class it should be fine.
Lastly, if that doesn't work you can turn on Spring debug logs with something like:
log4j.category.org.springframework.web=ALL
in your log4j.properties file.

Categories