Angularjs pass null value in file multipart FormData to Spring MVC - java

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"

Related

Jsp File upload returns empty Input Stream for file upload

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) {}
});
}

How to read two json objects in spring boot controller

I am new to java, i am trying to pass two json objects from ajax call to controller class... but i am getting below exception
Resolved exception caused by Handler execution: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String com.controllers.EmployeeController.saveData(java.lang.String,com.entities.EmployeeData,org.springframework.validation.BindingResult)
Jquery Code:
$("#saveData").submit(function(event) {
var data = [];
var formData = {};
var myJsonString;
var slab, lower;
$("table tbody tr").each(function(index) {
//alert(index);
slab = $(this).find('.slab').val();
lower = $(this).find('.lower').val();
if(slab != undefined && lower != undefined){
var form1 =new Object();
form1.slab=slab;
form1.lower=lower;
data.push(form1);
}
});
var form = this;
event.preventDefault();
$.each(this, function(i, v){
var input = $(v);
formData[input.attr("name")] = input.val();
});
var url = "/Portal/SaveData";
ajaxCall(url,formData,data);
});
function ajaxCall(url,formData,data){
//alert("AjaxPost!!!"+url);
// DO POST
$.ajax({
type : "POST",
contentType : "application/json",
url : url,
data : JSON.stringify({formData:formData,value:data}),
dataType : 'json',
beforeSend: beforeSendHandler,
success : function(response) {
alert("Success");
}else{
alert("else");
}
},
error : function(e) {
bootbox.alert({
size: "small",
title: "ALERT",
message: "There seems to be some problem while Proccessing record!"
})
}
});
}
Controller Method:
#RequestMapping(value = "/SaveData", method = RequestMethod.POST)
public #ResponseBody String saveData(#RequestBody String value,#Valid #RequestBody EmployeeData emp, BindingResult result) {
System.out.println(" Creating!!!");
//logic here
}
Where is the mistake in ajax call or in controller file?
there is any other way to pass multiple json objects to controller class file?
The #RequestBody annotation is expected to map to one and only one parameter in your method and to contain the entire contents of the incoming request.
You should map all your form data into a single JSON object, then handle that as a single JSON object in the backend as well.

Spring MultipartFiles duplicates file in mixed multipart

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

Angularjs to spring controller

I am fairly new to angularjs. Now I have a checkbox in one of my views
<input id="{{fields[3].name}}" type="checkbox" value="{{fields[3].name}}" ng-checked="selection.indexOf(fields[3].name) > -1" ng-click="toggleSelection(fields[3].name)" class="col-xs-1" />
In spring controller(like below) how do I check if the checkbox was checked?
#RequestMapping(value = "/test/{id}/accept", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public #ResponseBody ResponseBean acceptData(
#PathVariable("id") String id,
#RequestBody AcceptPayload payload, Model model,
HttpServletRequest request, HttpServletResponse response) {
....
}
Any code examples will help
You will need to send the checked data from your webpage to the controller.
The web page will have ng-repeat as below. The variable list contains the json array:
<tr ng-repeat="obj in list">
<td>
<input id="{{obj.name}}" type="checkbox" value="{{obj.name}}"
checklist-model="checkboxes" ng-checked="selection.indexOf(obj.name) > -1"
ng-click="toggleSelection(obj.name)" />
</td>
</tr>
The toggleSelection() function will get the array of selected obj.name and add it to variable array **selectedItems **:
$scope.selectedItems = [];
$scope.toggleSelection = function toggleSelection(name) {
var idx = $scope.selectedItems.indexOf(name);
// is currently selected
if (idx > -1) {
$scope.selectedItems.splice(idx, 1);
}
// is newly selected
else {
$scope.selectedItems.push(name);
}
};
The way in which I would hit the controller in the webpage would be below:
$scope.execute = function() {
$http({
method : 'GET',
url : 'trigger',
params : {
selectedItems : $scope.selectedItems
}
}).success(
function(data, status, headers, config) {
}).error(
function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
};
The method in controller would look like:
#RequestMapping(value = "/trigger", method = RequestMethod.GET, headers = "Accept=application/json")
public #ResponseBody String trigger(#RequestParam("selectedItems") List<String> selectedItems) throws NumberFormatException, Exception {
// Your method
}
You can change the method from GET to POST as you would want it.
AngularJS is a front end MVC library. The spring controller is back-end. This means that the value has to be sent from the front to the back over an HTTP request.
For example, in HTML,
ng-checked="someVariable"
can be captured in the angular app controller within the scope with
$scope.someVariable
This value has then to be sent over network to your Spring controller through HTTP request. Something like,
$http({ method: 'POST', data: 'someVariable' url: '/someUrl'})
.then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Please refer to https://docs.angularjs.org/api/ng/service/$http

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.

Categories