Cannot upload file using Angular and Spring Boot - java

I'm trying to upload a file with AngularJS and Spring Boot controller. I have 2 problems:
1) When I use an HTML form 'submit' I get exceeded size even though I have set the max size of file to 128M. The code looks like this:
public class AppConfig {
#Bean
public MultipartConfigElement multipartConfigElement() {
factory.setMaxFileSize("128MB");
factory.setMaxRequestSize("128MB");
return factory.createMultipartConfig();
}
}
It seems that Spring ignores these settings.
2) When I'm trying to upload a file, I get the error:
org.springframework.web.multipart.MultipartException: The current request is not a multipart request
The Angular controller looks like this:
$scope.uploadFile=function(){
var formData=new FormData();
formData.append("file",file.files[0]);
$http.post('/content-files/upload /', file.files[0], {
transformRequest: function(data, headersGetterFunction) {
return data; // do nothing! FormData is very good!
},
headers: {'Content-Type': undefined }
})
.success(function(){
console.log('Post Succeded !');
})
.error(function(){
console.log('Post Failed .');
});
}
and the Spring controller looks like this:
#RequestMapping(value = "/content-files/upload/", method = RequestMethod.POST )
public #ResponseBody String handleFileUpload( #RequestParam("file") MultipartFile file) {
System.out.println("BrandController.uploadMultipart()");
String name=file.getName();
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream =
new BufferedOutputStream(new FileOutputStream(new File(name)));
stream.write(bytes);
stream.close();
return "You successfully uploaded " + name + "!";
} catch (Exception e) {
return "You failed to upload " + name + " => " + e.getMessage();
}
} else {
return "You failed to upload " + name + " because the file was empty.";
}
}
I tried changing the JS controller to:
$http.post('/content-files/upload /', file.files[0], {
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
})
but I get the same error as above. When I try changing the JS controller to:
headers: { 'Content-Type': 'multipart/form-data' },
transformRequest: angular.identity
I get the error the request was rejected because no multipart boundary was found.
It seems that I have tried all combinations with the parameters, and still nothing worked. What do I need to do to get this file upload to work?

Try with this:
var formData = new FormData();
formData.append('file',file.files[0]);
$http.post('/content-files/upload /', formData, {
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
})

Based on the MultipartAutoConfiguration code, the way to increase the size limit on uploads (by default max is 1 MB) with Spring Boot is the following properties:
multipart.maxFileSize=129Mb
multipart.maxRequestSize=129Mb
Regarding jquery multipart uploads, the way you are doing it does not appear correct, there are other good references that you can check for, I have seen plenty within Stackoverflow itself.

Related

SyntaxError: Unexpected token s in JSON at position 0 at JSON.parse + angularjs

I am working with springmvc and angularjs. I'm trying to send the String response from springmvc controller to the angular controller, but facing the below error message shown on the browser console and the response which returned from springmvc is not getting printed in the angularjs side.
ERROR:
SyntaxError: Unexpected token s in JSON at position 0
at JSON.parse ()
Sample code:
js:
myApp.controller('myTestCtrl', function ($rootScope, $scope,MyService) {
$sco[e.submitInfo = function(){
var data = new FormData();
var allInfo =
{
'name': $scope.name,
'id': $scope.id,
'product': $scope.product,
'message':$scope.message
}
//files to be attached if exists
for (var i in $scope.filesAttached) {
var file = $scope.filesToAttach[i]._file;
data.append("file", file);
}
MyService.sendAllInfo(data).then(
function (response) {
if (response === "") {
alert("success");
//logic
}else{
alert("in else part " + response);
}},
function (errResponse) {
console.log("Error while retrieving response " + errResponse);
});
};
});
}});
MyService:
myService.sendAllInfo = function (data) {
var deferred = $q.defer();
var repUrl = myURL + '/myController/allInfo.form';
var config = {
headers: {'Content-Type': undefined},
transformRequest: []
}
$http.post(repUrl,data,config)
.then(
function (response) {
alert("response json in service: "+ response);
deferred.resolve(response.data);
},
function(errResponse){
console.error('Error while getting response.data'+ errResponse);
deferred.reject(errResponse);
}
);
return deferred.promise;
};
Spring mvc:
#RequestMapping(value = "/allInfo", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE)
public #ResponseBody
String allInfoData(#RequestParam("data") String data,#RequestParam("file") List<MultipartFile> files){
//logic
return "success";
}
In my above spring controller code, i'm returning success string to angularjs controller, but in the browser the below error is displayed.
SyntaxError: Unexpected token s in JSON at position 0
at JSON.parse ()
Note: Above is only the sample code , it is perfectly hitting the spring controller and issue is only while catching the response from spring controller to angular controller.
I tried to change produces=MediaType.TEXT_PLAIN_VALUE to produces={"application/json"} but still it is showing the same error.
To avoid parsing the string, use transformResponse: angular.identity:
myService.sendAllInfo = function (data) {
̶ ̶v̶a̶r̶ ̶d̶e̶f̶e̶r̶r̶e̶d̶ ̶=̶ ̶$̶q̶.̶d̶e̶f̶e̶r̶(̶)̶;̶
var repUrl = myURL + '/myController/allInfo.form';
var config = {
headers: {'Content-Type': undefined},
transformRequest: [],
//IMPORTANT
transformResponse: angular.identity
}
var promise = $http.post(repUrl,data,config)
.then(
function (response) {
alert("response json in service: "+ response);
return response.data;
},
function(errResponse){
console.error('Error while getting response.data'+ errResponse);
throw errResponse;
}
);
return promise;
};
Also avoid using the deferred Anti-Pattern.
In the response there are some values which are simple text not String so You're asking it to parse the JSON text something (not "something"). That's invalid JSON, strings must be in double quotes.
If you want an equivalent to your first example:
var s = '"something"';
var result = JSON.parse(s);
The best solution is use responseType: "text" as "json" it will woke

File size is zero when passing multiple files to the Spring Controller

I am trying to post a file (single file or multiple files) along with some JSON data using AngularJS and Spring MVC.
I tried as shown below:
JS:
(function () {
'use strict';
var myApp = angular.module('app');
myApp.controller('filesWithJSONController', function ($scope, fileUploadService) {
$scope.uploadFile = function () {
var file = $scope.myFile;
var uploadUrl = myApplnURL + '/showInfo/getInformationTest';", //Url of web service
var fd=new FormData();
angular.forEach($scope.files,function(file){
fd.append('file',file);
});
fd.append('properties', new Blob([JSON.stringify({
"name": "root",
"password": "root"
})], {
type: "application/json"
}));
promise = fileWithJSONService.sendInformation(fd,uploadUrl);
promise.then(function (response) {
$scope.serverResponse = response;
}, function () {
$scope.serverResponse = 'An error has occurred';
})
};
});
})();
(function () {
'use strict';
var myApp = angular.module('app');
myApp.service('fileWithJSONService', function ($http, $q) {
this.sendInformation = function (fd, uploadUrl) {
var deffered = $q.defer();
var config = {
headers : {
'Content-Type': undefined
}
}
$http.post(uploadUrl, fd, config).then(function (response) {
console.log("response " + response);
}, function (errResponse) {
console.error('Error in request' + errResponse);
deferred.reject(errResponse);
});
...
Spring Controller:
#Controller
#RequestMapping("/showInfo")
public class InfoController{
#RequestMapping(value = "/getInformationTest", method = RequestMethod.POST, consumes = {"multipart/form-data"})
#ResponseBody
public String sendInformationTest(#RequestPart("properties") ConnectionProperties properties, #RequestPart("file") List<MultipartFile> multiPartFileList){
System.out.println("In spring controller");
//business logic
}
With the above code, it is showing the multiPartFileList size as zero in Spring Controller.
But if I change the code to take only one file instead of multiple files, it is showing the file information successfully. Any input?
try with:
var fd = new FormData();
fd.append('file', file);//replace with forEach
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,//overrides Angular's default serialization, leaving our data intact.
headers: {'Content-Type': undefined}//lets the browser detect the correct Content-Type as multipart/form-data, and fill in the correct boundary.
})
.success(function(){})
.error(function(){});
Backend - Spring:
#RequestMapping(value ="/upload", method = RequestMethod.POST)
public ResponseEntity handleFileUpload(#RequestParam("file") MultipartFile[] files){
//add the others params & logic
}

How to pass csv file to sever path from ajax post to spring mvc controller?

I am trying to download csv file and wanted to upload that same csv file into my server location path using Spring MVC and through Ajax Post request on executing my application.
From the below code, I can able to download my csv file on running my application, but it is not uploading into my server location path at the same time or simultaneously on executing of the application, I am not sure why it is not uploading. Please help me to upload my file at my given path. Thanks !
js:
function download_csv(csv, filename) {
//filename = test.csv
//csv = "testname,testid
hello,10"
var csvFile;
var downloadLink;
// CSV FILE
csvFile = new Blob([csv], {type: "text/csv"}); //[object Blob]
// Download link
downloadLink = document.createElement("a");
// File name
downloadLink.download = filename;
var formData = new FormData(csvFile);
console.log(formData);//FormData {}
$.ajax({
url: "/uploadFile",
type: "POST",
//data: filename,
// data: new FormData(csvFile),
data: formData,
// enctype: 'multipart/form-data',
processData: false,
contentType: false,
cache: false,
success: function (data) {
// Handle upload success
$("#upload-file-message").text("File succesfully uploaded");
},
error: function (errordata) {
console.log("error: "+errordata);//[object Object]
console.log("error data: "+JSON.stringify(errordata));
}
});//$.ajax()
// We have to create a link to the file
downloadLink.href = window.URL.createObjectURL(csvFile);
// Make sure that the link is not displayed
downloadLink.style.display = "none";
// Add the link to your DOM
document.body.appendChild(downloadLink);
// Lanzamos
downloadLink.click();
}
controller:
#Controller
public class MainController {
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<?> uploadFile(
#RequestParam("filename") MultipartFile uploadfile) {
try {
// Get the filename and build the local file path
String filename = uploadfile.getOriginalFilename();
String directory = env.getProperty("paths.uploadedFiles");
String filepath = Paths.get(directory, filename).toString();
// Save the file locally
BufferedOutputStream stream =
new BufferedOutputStream(new FileOutputStream(new File(filepath)));
stream.write(uploadfile.getBytes());
stream.close();
}
catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}
application.resources:
paths.uploadedFiles = /resources/test/
POST http://localhost:8000/uploadFile 400 (Bad Request)
error data: {"readyState":4,"responseText":"{\"timestamp\":1511523835282,\"status\":400,\"error\":\"Bad Request\",\"exception\":\"org.springframework.web.bind.MissingServletRequestParameterException\",\"message\":\"Required MultipartFile parameter 'filename' is not present\",\"path\":\"/uploadFile\"}","responseJSON":{"timestamp":1511523835282,"status":400,"error":"Bad Request","exception":"org.springframework.web.bind.MissingServletRequestParameterException","message":"Required MultipartFile parameter 'filename' is not present","path":"/uploadFile"},"status":400,"statusText":"Bad Request"}

Trouble downloading binary file with angular $http or jquery.ajax

My problem is that I am getting the wrong sized file on the client side. Here is my #Controller ...
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public ResponseEntity<?> download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
try {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
ResponseEntity<InputStreamResource> result;
return ResponseEntity.ok()
.contentLength(Files.size(zippath))
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(new FileInputStream(zippath.toFile())));
} catch (Exception ex) {
// ErrorInfo is another class, unimportant
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorInfo(ex));
}
}
... and here is my client-side code using angular-file-saver ...
$http({url: "export/download/" + exportitem.exportId, withCredentials: true})
.then(function(response) {
function str2bytes(str) {
var bytes = new Uint8Array(str.length);
for (var i=0; i<str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
var blob = new Blob([str2bytes(response.data)], {type: 'application/octet-stream'});
FileSaver.saveAs(blob, "download.zip");
}, $exceptionHandler);
The original file is 935673 bytes but response.data is 900728 and passing it through the transformation to Uint8Array results in a Blob that is 900728 in size as well. Either way, the resulting saved file is 900728 bytes (34945 bytes shy). Also it is not quite the same in what gets written. It seems to slightly get bloated but then the last part just seems to be truncated. Any ideas what I might be doing wrong?
UPDATE
I just updated my controller method to be the following and got the exact same result. Grrr.
#RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public void download(final HttpServletRequest request,
final HttpServletResponse response,
#PathVariable("id") final int id) throws IOException {
// Pseudo-code for retrieving file from ID.
Path zippath = getZipFile(id);
if (!Files.exists(zippath)) {
throw new IOException("File not found.");
}
response.setContentType("application/zip");
response.setHeader("Content-Disposition",
"attachment; filename=download.zip");
InputStream inputStream = new FileInputStream(zippath.toFile());
org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
}
So the problem turned out to be angular's $http service. I also tried jQuery's ajax method. Both gave the same result. If I instead use the native XMLHttpRequest it works correctly. So the Java code was sound. I first verified this by exposing the file directly to the internet and then both using curl and directly accessing in the browser I managed to download the file of the correct size. Then I found this solution so that I could also download the file via javascript.
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.onreadystatechange = function (){
if (xhr.readyState === 4) {
var blob = xhr.response;
FileSaver.saveAs(blob, filename);
}
};
xhr.send();
Why does angular or jQuery give the wrong result? I still don't know but if anyone wishes to give an answer that uses those it would be appreciated.
responseType: blob
did the trick for a zip file
Angular 2 +
this.http.get('http://localhost:8080/export', { responseType: ResponseContentType.Blob })
.subscribe((res: any) => {
const blob = new Blob([res._body], { type: 'application/zip' });
saveAs(blob, "fileName.zip");
i just stumbled over the 'responseType' in $http requests, you are probably looking for 'blob': https://docs.angularjs.org/api/ng/service/$http#usage

receiving json from message body

I'm trying to send a json String from an ajax call to a jersey web service. I've looked at many related questions and articles but I haven't been able to get anything to work. When I watch my calls from fiddler I can see the json in the body but when the method is hit the String is blank. Thanks for any help.
getFile: function() {
var urlXls = 'http://localhost:8080/KodiakWebS/Kodiak/KodiakXls/generateXls';
//var json = '{"xls":[{"name":"Pokemon","row":[{"data":"Lugia"},{"data":"Charzard"}]},{"name":"Types","row":[{"data":"Special"},{"data":"Fire"}]}]}'; //encodeURI();
var json = this.get('tempJSON');
urlXls = urlXls.replace(/\s/g, "");
$.ajax({
url: urlXls,
type: "POST",
data: json,
contentType: 'application/json; charset=utf-8', // ;charset=utf-8',
success: function(json, status)
window.location.assign(json.url);
alert("yay");
},
error: function(xhr, err) {
debugger;
alert(xhr+ " || " + err);
}
});
},
#POST
#Path("/generateXls")
#Consumes("application/json")
#Produces({ "application/xls" })
public Response sendWorkBook(final String json) throws Exception {
createWorkbook(json);
FileOutputStream sprdSht = new FileOutputStream("WorkBook.xls");
wb.write(sprdSht);
sprdSht.close();
System.out.println("all done");
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream outPut)
throws IOException,
WebApplicationException {
try {
wb.write(outPut);
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
};
return Response.ok(stream).header("content-disposition", "attachment; filename = egisDoc.xls").build();
}
If you say you consume application/json, then I think you need to provide a Java object to model the JSON you are supplying.
If you do just want the JSON as a String, then you need to consume text/plain.
I typically have this when I use JAXRS:
#PUT
#Path("entities")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public SomeDomainObject updateSomeEntity(SomeDomainObject someDomainObject) {
// Whatever...
}
Where "SomeDomainObject" is just a POJO with getters and setters.
I normally use the JacksonJsonProvider implementation rather than the JAXB one, and the above style of controller works just fine with JSON marshalling for me. I don't even need to add any JSON annotations to the domain objects either.
I was able to get it working thanks to the suggestions from caprica and tinkering around a little bit. Not much changed in my code but here it is.
#PUT
#Path("/generateXls")
#Consumes("text/plain")
#Produces({ "application/xls" })
public Response sendWorkBook(String json) throws Exception {
System.out.println("json: " + json);
createWorkbook(json);
getFile: function() {
var urlXls = 'http://localhost:8080/KodiakWebS/Kodiak/KodiakXls/generateXls';
//var json = '{"xls":[{"name":"Pokemon","row":[{"data":"Lugia"},{"data":"Charzard"}]},{"name":"Types","row":[{"data":"Special"},{"data":"Fire"}]}]}'; //encodeURI();
var json = this.get('tempJSON');
urlXls = urlXls.replace(/\s/g, "");
$.ajax({
type: "PUT",
url: urlXls,
data: json,
contentType: 'text/plain; charset=utf-8', // ;charset=utf-8',
success: function(json, status) {
window.location.href = json.url;
//window.location.assign(json.url);
//alert("yay");
},
error: function(xhr, err) {
debugger;
alert(xhr+ " || " + err);
}
});
},
now on to trying to download xls file my service creates, hopefully I won't need to ask how to get what I have working (but if anyone has a method they're proud of and would like to share you're more than welcome too).
Thanks for the help.

Categories

Resources