I have UI code like this
.factory('Service', function ($http, $resource, BASE_PATH) {
function sendResponse (code, responses) {
return $http({
method: 'POST',
url: "/test",
headers: {'Content-Type': undefined},
transformRequest: function (data) {
var formData = new FormData();
formData.append("dec", new Blob([angular.toJson(data.dec)], {type: 'application/json'}));
angular.forEach(data.files, file => {
formData.append("file", file);
});
return formData;
},
data: {dec: responses.dec, files: responses.files}
});`enter code here`
}
and here is controller part
#RequestMapping(value = "/test", method = POST)
public ResponseEntity<List<Test>> save(#RequestPart(value = "file", required = false) MultipartFile[] multipartFiles,
#RequestPart(value = "dec") List<Decision> dec) {
return save(multipartFiles, dec);
}
The problem is, MultipartFile has duplicated files. For example I attach one "test.txt" but, I see two "test.txt" files in controller while debugging.
Is there any way to solve this issue?
I was using zuul server in between as API gatway. I found there is a bug in old version of zuul which duplicated one part of multipart object.
The solution can be referred here
github.com/spring-cloud/spring-cloud-netflix/issues/1171
Related
I am working on a jsp file upload using ajax call and I am able to hit the java controller in the HttpServletRequest I see the multipart file received but when I do request.getInputStream.readAllBytes(), I get an empty byte array
The ajax call in javascript
function saveFileUpload() {
var data = new FormData()
data.append("file", document.getElementById(fileName).files[0])
$.ajax({
type: 'POST',
data: data,
url: pageContext + "/upload",
processData: false,
contentType: false,
success: function(data) {},
error: function(e) {}
});
}
}
In Java controller
#RequestMapping(value = {"/upload"}, method = RequestMethod.POST)
public void fileUpload (
HttpServletRequest request, HttpServletResponse response){
byte[] arr = request.getInputStream().readAllBytes();
System.out.println(arr.length);
}
The above code prints arr.length as 0. Can someone tell me the reason for this issue?
I assume that you are using Springboot and your application supports multipart/form-data. After version 3.0 the Servlet API natively support it.
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public void fileUpload(#RequestParam("file") MultipartFile file) throws IOException
{
if (!file.isEmpty())
{
byte[] bytes = file.getBytes();
// and so on
}
}
JQuery's Ajax:
function saveFileUpload() {
let data = new FormData()
data.append("file", document.getElementById(fileName).files[0])
$.ajax({
type: 'POST',
data: data,
url: pageContext + "/upload",
processData: false,
contentType: false,
success: function(data) {},
error: function(e) {}
});
}
In a Spring application I expose a controller through REST with the following code, but List files is always empty.
What is the problem?
#PostMapping(value = "/uploadMultipleFiles", consumes = {MediaType.MULTIPART_MIXED_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity uploadMultipleFiles(#AuthenticationPrincipal UserPrincipal principal,
#RequestPart String collection,
#RequestPart (value="files", required = true) final List <MultipartFile> files) {
Some logic...
}
In React.JS I send the request with this code:
uploadFiles(e) {
const uploadRequest = {
collection: "prova"
};
const formData = new FormData();
formData.append('collection', new Blob([JSON.stringify("some value from form")], {
type: "application/json"
}));
for (let i = 0; i < this.state.files.size; i++) {
formData.append('files', this.state.files[i]);
}
uploadPhotos(formData)
.catch(error => {
console.log(error)
});
}
uploadPhotos method is the following:
export function uploadPhotos(photoData) {
return request({
url: API_BASE_URL + "/photo_submit/uploadMultipleFiles",
method: 'POST',
body: photoData
});
}
Best to upload files one by one, show a success/fail for each file. Otherwise you would have to zip/archive a whole bunch of files, and unzip them on the other end, and it is more error prone if one file is bad then the whole upload might not work.
I am attempting to send form data with Multipart data (Image) to Spring MVC controller. From my form. image field is non mandatory. so when i call spring controller without input image field, i get below error in browser console.
Failed to load resource: the server responded with a status of 400
(Bad Request)
Html Code:
<input file="file" type="file" id ="file2"/>
AngularjS Code:
$scope.saveData = function (formObj) {
$http({
url: CONTEXT_PATH+'saveFile',
method: "POST",
headers: { 'Content-Type': undefined },
transformRequest: function (data) {
alert(data.files);
var formData = new FormData();
formData.append("model", angular.toJson(data.model));
formData.append("file", data.files);
return formData;
},
data: { model: formObj, files: $scope.file }
}).then(function (response) {
//alert(response);
});
};
app.directive('file', function () {
return {
scope: {
file: '='
},
link: function (scope, el, attrs) {
el.bind('change', function (event) {
var file = event.target.files[0];
scope.file = file ? file : undefined;
scope.$apply();
});
}
};
});
Spring Controller Code:
#RequestMapping(value = "/saveFile")
public #ResponseBody String storeAd(#RequestParam ("model") String adString, #RequestParam ("file") MultipartFile file) throws IOException {
System.out.println("adString > "+adString);
return "OK";
}
When the image is not uploaded, the request is bad because Spring MVC assumes all parameters required unless otherwise defined.
In your case, you should add required = false.
#RequestParam(value = "file", required = false)
Your server code is expecting a request parameter with name "file" but you are not defining it properly.
Change
<input file="file"
To
<input type="file" name="file"
FrontEnd: jsp with AngularJS
BackEnd: Spring MVC/Java
I am uploading a file using ng-flow, angularJS. Source: https://github.com/flowjs/ng-flow
File upload is successful. I need to return a json from my Spring Controller. Any clues how to go about it?
P.S. can't find where to put in .success() function, if at all that is applicable.
Spring Controller:
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public String uploadFileHandler(#RequestParam("file") MultipartFile file, Model model) {
//Upload file and process
JsonObject jo = Json.createObjectBuilder().add(path, folderPath.toString())
.add(aContentsAttrib, aContents)
.add(bContentsAttrib, bContents).build();
}
app.js code:
(function() {
var app = angular.module('app', ['flow'])
.config(['flowFactoryProvider', function (flowFactoryProvider) {
flowFactoryProvider.defaults = {
target: 'upload',
permanentErrors: [404, 500, 501],
maxChunkRetries: 4,
chunkRetryInterval: 500,
simultaneousUploads: 4
};
flowFactoryProvider.on('catchAll', function (event) {
console.log('catchAll', arguments);
});
// Can be used with different implementations of Flow.js
// flowFactoryProvider.factory = fustyFlowFactory;
}]);
app.controller('PageController', function() {
//this.products = gems;
});
app.controller("TabController", function() {
this.tab = 1;
this.showOutput = false;
this.viewEvents = false;
this.isSet = function(checkTab) {
return this.tab === checkTab;
};
this.changeVal = function() {
this.viewEvents = true;
};
this.setTab = function(setTab) {
this.tab = setTab;
};
});
})();
What exactly should be returned from the spring controller? (String/#ResponseBody String etc)
How to collect that json in angular?
On your controller #ResponseBody should be added and the jo returned as String:
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public #ResponseBody String uploadFileHandler(#RequestParam("file") MultipartFile file, Model model) {
//Upload file and process
JsonObject jo = Json.createObjectBuilder().add(path, folderPath.toString())
.add(aContentsAttrib, aContents)
.add(bContentsAttrib, bContents).build();
return jo.toString();
}
In AngularJS, you should do this for being able to post files and then retrieve the data back:
$http({url: '/url',
method: 'POST',
data: $scope.myFile,
headers: {'Content-Type': undefined },
transformRequest: angular.identity
}).success(data){
$scope.myData = data;
});
In your Spring controller you should just return an Object containing the properties you want to transfer to your angular service. This will be automatically (or by default) be converted to JSON. #RequestBody is not needed.
This return value will be available in the success callback, something like:
$http({
method: 'POST',
url: '...',
}).success(function (data) {
//data is your JSON response
})},
If you are using Spring 3 you can do this
#RequestMapping(value = "/getDealers", value = "/upload", method = RequestMethod.POST)
#ResponseBody
public String uploadFileHandler() {
}
#ResponseBody annotation directly writes the response to the response stream. You would not need a JSP. Just send the request for the controller from the browser & the controller method will write the response to the response stream.
You can parse the response using Jquery or any json library & display in the JSP
Check this out
An alternate way, which I just found out. Will be useful to extract from existing code, without any modification. It does introduce an extra global variable, outside your main angular app, and might not be highly recommended, but still, posting this.
var json = {};
var app = angular.module('app', ['flow'])
.config(['flowFactoryProvider', function (flowFactoryProvider) {
flowFactoryProvider.defaults = {
target: 'processxls',
permanentErrors: [404, 500, 501],
maxChunkRetries: 4,
chunkRetryInterval: 500,
simultaneousUploads: 4
};
flowFactoryProvider.on('catchAll', function (event) {
console.log('catchAll', arguments);
this.jsonResponse = arguments[2]; //Note this change
//json = this.jsonResponse;
console.log(this.jsonResponse);
json = angular.fromJson(this.jsonResponse);
});
// Can be used with different implementations of Flow.js
// flowFactoryProvider.factory = fustyFlowFactory;
}]);
'json' variable now has the json response received. You can use it for further use now.
P.S. in order to check for which value of 'i' arguments[i] gives you the json, see console.
I have the following code that works:
#RequestMapping(value = "/jsonasclass", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody
ContactVO jsonAsClassPost(#RequestBody ContactVO ct){
ct.setFirstName("This-property-is-changed-in-the-controller");
return ct;
}
and the corresponding ajax call by post:
$.ajax({
url: '/jsonasclass/',
type: 'POST',
dataType: 'json',
data: JSON.stringify({
id:1,
userID:1.1,
login:'sample-login',
firstName:'sample-first-name'
}),
contentType: 'application/json',
mimeType: 'application/json',
success: _callBack,
error: _errorCallback
});
Now I want to achieve the same thing, but I want to do it by GET. Anyone knows how?
I have tried changing POST to GET (in both controller and ajax call) but it did not work.
The error I get: description The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
Thanks to RaulRene's comment here is how you would do it.
get rid of #RequestBody from the controller and change method to get.
send the properties of the class in the controller as browser variables and spring will automatically map them to the class.
Here is the solution:
#RequestMapping(value = "/jsonasclass", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody
ContactVO jsonAsClassGet(ContactVO ct){
ct.setFirstName("This-property-is-changed-in-the-controller");
return ct;
}
and corresponding ajax:
$.ajax({
url:'/jsonasclass/',
type: 'GET',
data: {
id:1,
userID:1.1,
login:'sample-login',
firstName:'sample-first-name'
},
success: _callBack,
error: _errorCallback
});