AngularJS Formdata file array upload - java

I'm trying to upload (actually POST) numerous small files in one go along with some key, value pairs:
$scope.uploadFiles = function(files) {
if (files.length === 0) {
return;
}
var formData = new FormData();
formData.append('keyName1', 'keyValue1');
formData.append('keyName2', 'keyValue2');
formData.append('keyName3', 'keyValue3');
for (var i = 0; i < files.length; i++) {
formData.append('files[]', files[i]);
}
$http.post( '/myEndpoint', formData, {
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (result) {
console.log('YAY');
}).error(function () {
console.log('NAY');
});
}
Here's the Java backend:
#RequestMapping(value = "/myEndpoint", method = RequestMethod.POST)
#ResponseBody
public void uploadFiles(
#RequestParam("files") List<MultipartFile> fileList,
#RequestParam("keyName1") String keyName1,
#RequestParam("keyName2") String keyName2,
#RequestParam("keyName3") String keyName3,
HttpServletResponse response, HttpSession session) throws Exception {
log.debug(fileList.size()); // Always logs zero
}
The endpoint is being hit but the filesList length is zero. I've also changed
List<MultipartFile> fileList to MultipartFile[] filesArray
but that didn't work wither.
Can anyone shed some light please?
Cheers,
Paul

This might be helpful to you.
Angular:
$scope.uploadFiles = function(files) {
if (files.length === 0) {
return;
}
var formData = new FormData();
formData.append('keyName1', 'keyValue1');
formData.append('keyName2', 'keyValue2');
formData.append('keyName3', 'keyValue3');
for (var i = 0; i < files.length; i++) {
formData.append('file'+i, files[i]);
}
$http.post( '/myEndpoint', formData, {
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (result) {
console.log('YAY');
}).error(function () {
console.log('NAY');
});
}
On Spring/Java Side:
RequestMapping(value = "/myEndpoint", method = RequestMethod.POST)
public #ResponseBody Object uploadFiles(MultipartHttpServletRequest request, HttpServletResponse response) throws IOException {
//do stuff here...
final String keyName1= request.getParameter('keyName1');
//and so on......
Iterator<String> iterator = request.getFileNames();
MultipartFile multipartFile = null;
while (iterator.hasNext()) {
multipartFile = request.getFile(iterator.next());
//do something with the file.....
}
}
BTW, on you angular side, you can always end the file on one go or with multiple request. It's up to you how you want that implemented.

I used a library on GitHub to help me accomplish this task with my Java Glassfish Server.
https://github.com/nervgh/angular-file-upload
I only needed to upload a single file, and here is the Java Backend to receive that file. This framework does have support to upload multiple files to the server.
#POST
#Path("/Endpoint")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response whateverEndPoint(#FormDataParam("fileName") InputStream fileInputStream,
#FormDataParam("fileName") FormDataContentDisposition contentDispositionHeader,
#FormDataParam("additionalParameter") String additionalParameter) {
System.out.println(contentDispositionHeader.getFileName());
String output = "File Received on the server: ";
return Response.status(200).entity(output).build();
}
Here is my angular controller that uses the framework:
angular.module('AppThing').controller('DemoController',function($rootScope,$scope,FileUploader){
//creating the uploader with the correct url
$scope.uploader = new FileUploader({
url : 'Endpoint',
method : 'POST',
alias: 'fileName',
autoUpload:true
});
//runs right after we add a file to the queue
$scope.uploader.onAfterAddingFile = function(item){
};
//runs right before we upload an item to the server
$scope.uploader.onBeforeUploadItem = function(item){
console.log('This is just before the image upload');
item.formData.push({'additionalParameter': $rootScope.additionalParameter});
};
$scope.uploader.onSuccessItem = function(item, response, status, headers) {
};
});
Hope this helps

Related

405 Error : multipart/form-data with Spring

I am trying to send a Json string along with multiple files into my Spring Controller, however it would always give me a 405 Method Not Allowed Error, what am I doing wrong?
Javascript Code:
var formdata = new FormData();
formdata.append('user', JSON.stringify(userData));
files.forEach(file=> {
formdata.append('files', file);
});
jQuery.ajax({
url: "user/submitForm",
type: "POST",
data: formdata,
enctype: 'multipart/form-data',
processData: false,
contentType: false,
success: function (data)
{
console.log("SUCCESS");
},
error: function (request, status, error) {
alert(status + " : " + JSON.stringify(request));
}
});
Controller in Spring:
#PostMapping(value= "/submitForm", consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<?> userRegistration( #RequestPart("user") String user,
#RequestPart("files") List<MultipartFile> files, BindingResult bindingResult) {
ObjectMapper obj = new ObjectMapper();
User newUser = new User();
newUser = obj.readValue(user, User.class);
System.out.println("User : \n"+ newUser.toString());
System.out.println("Files : \n"+ files.toString());
return null;
}
This was the solution that I found from Antonio112009's answer
SOLUTION
#PostMapping(value = "/submitForm")
public ResponseEntity<?> userRegistration(
#RequestParam("user") String user,
#RequestParam(value = "files", required = false) List<MultipartFile> files) {
ObjectMapper obj = new ObjectMapper();
User userObj = new User();
.
.
.
}
I use another solution, who works as expected and are a bit more flexible from my point of view.
Front-end part is in Typescript.
Front-end
var formData = new FormData();
options.files.forEach(function (file) {
formData.append(file.name, file);
});
formData.append("myParam", "coucou");
var xhr = new XMLHttpRequest();
xhr.open("POST", "/rest/upload");
xhr.onload = function () {
var data = JSON.parse(xhr.responseText);
options.callback("success", options.files.map(function (file) {
return {
file: file,
content: data[file.name]
};
}));
};
xhr.send(formData);
Back-end (Java Spring)
#RestController
#RequestMapping(value = "/rest")
public class UploadController {
#PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Boolean> upload(MultipartHttpServletRequest request) {
// Get param
Object myParam = request.getParameter("myParam");
// Get iteretaor on all files
Iterator<String> iterator = request.getFileNames();
MultipartFile multipartFile = null;
while (iterator.hasNext()) {
multipartFile = request.getFile(iterator.next());
final String fileName = multipartFile.getOriginalFilename();
final String fileSize = String.valueOf(multipartFile.getSize());
// Add logic ....
}
}
return new ResponseEntity(true);
}

List of files through REST from ReactJS is empty in Spring application

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.

get Header in jersey from a GET request

From a js page (in angular) I call a REST request, GET method, were I would to pass an header, this is the function that I call from the REST request:
allstaffworking: function(_getstaff){
var currentToken = _GetToken();
var Headers = {
token: currentToken.stringtoken
};
console.log("idtoken"+Headers);
if (currentToken !== null) {
$http({
method : 'GET',
headers: Headers,
url : REST_URL+'staff/working'
}).then(function successCallback(response) {
_getstaff(response)
}, function errorCallback(response) {
console.log(response.statusText);
});
} else {
console.log("NON SEI LOGGATO!!!");
}
},
Whithout headers: Headers, it works, but I want to pass an important json string: {"idtokenStaff":11,"staffType":{"idstaffType":2,"type":"Dipendente"},"tokenStaff":"88d08m8ve4n8i71k796vajkd01"} in the Headers. I don't know How I can take this string in Jersey. This is java file in with I have the REST method:
#Path("/staff")
public class StaffController {
BaseDao sDao = new StaffDaoImpl();
StaffDao stfDao = new StaffDaoImpl();
TokenStaffDao tsDao = new TokenStaffDaoImpl();
TokenStaff ts = new TokenStaff();
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Staff> getStaff()
{
List<Staff> listOfStaff=sDao.getAll(Staff.class);
return listOfStaff;
}
#GET
#Path("/working")
#Produces(MediaType.APPLICATION_JSON)
#Consumes("application/json")
public List<Staff> getWStaff(#HeaderParam("token") String token) throws JSONException
{
JSONObject jsonObj = new JSONObject(token);
Boolean id = tsDao.getExistence(jsonObj.getInt("idtokenStaff"));
if (id){
List<Staff> listOfWStaff=stfDao.getAllW();
return listOfWStaff;
}
else
return null;
}
}
Taking header from: #HeaderParam("token") String token. How Can I take the element of the header?
A bit late to answer this, but you can also use #Context annotation to get httpheaders.
Eg.
public List<Staff> getWStaff(#Context HttpHeaders httpHeaders) {
String token = httpHeaders.getHeaderString("token");
JSONObject jsonObj = new JSONObject(token);
}

Return Json from Spring Controller After Uploading File via AngularJS

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.

How to return a text file with Spring MVC?

#RequestMapping( method = RequestMethod.POST, value = DataController.RESOURCE_PATH + "/file", headers = "content-type=application/json" )
#ResponseBody
public void export( #RequestBody JSONObject json, HttpServletResponse response ) throws IOException
{
String myString = "Hello";
}
The string is generated inside the Controller.
What I want is to send back to the user a Window where he can save a file which contains the myString.
$.ajax({
type: "POST",
url: url,
data: JSON.stringify(createJSON()),
contentType: "application/json",
success: function(response)
{
console.log("Exported JSON: " + JSON.stringify(createJSON()));
console.log(response);
},
error: function()
{
console.log(arguments);
alert("Export process failed.");
}
});
It clearly doesn't work in this current state and I am stuck at the moment.
here is a sample:
#RequestMapping( method = RequestMethod.POST,
value = DataController.RESOURCE_PATH + "/file",
headers = "content-type=application/json" )
public void export( #RequestBody JSONObject json, HttpServletResponse response )
throws IOException {
String myString = "Hello";
response.setContentType("text/plain");
response.setHeader("Content-Disposition","attachment;filename=myFile.txt");
ServletOutputStream out = response.getOutputStream();
out.println(myString);
out.flush();
out.close();
}
PS: don't forget to put some random stuff in your url (as parameter for example) to ensure your browser does not cache the text file.
To return a file you need to use the MediaType.APPLICATION_OCTET_STREAM as the response type.
I recommend using filesaver.js.
Then your solution will look like:
var text = JSON.stringify(createJSON());
var blob = new Blob([text], {type: "text/plain; charset=utf-8"});
saveAs(blob, "myfile.txt");

Categories