File upload in Grails application using angular js - angularjs

I'm working on a Grails (2.3.7) application with AngularJS. I've to upload files in my application. Since the front end is managed by Angular , I'm uploading my file from Angular controller. I've gone through This
and this discussions , and tried to upload as follows.
My file uploader is
<input type="file" ng-file-select="onFileSelect($files)">
Angular controller is
myapp.controller('createWebController',['$scope','$http','$upload',function($scope,$http,$upload){
$scope.onFileSelect = function($files) {
var file = $files[0];
console.log(file)
$upload.upload({
url: 'UploadLogo/upload', //upload.php script, node.js route, or servlet url
file: file,
method: 'POST' ,
fileFormDataName: "myFile",
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
})
.error(function(data){ console.log(data)})
;
};
}])
on the server , I'm using this service and the upload handler code is
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.MultipartFile
import org.codehaus.groovy.grails.web.context.ServletContextHolder
class UploadLogoController {
FileUploadService fileUploadService
def upload() {
def avatarImage = request.getFile('file')
if (!avatarImage.isEmpty())
{
userInstance.avatar = fileUploadService.uploadFile(avatarImage, "logo.png", "~/Desktop/upload")
render "ok"
}
else
{
render "Empty"
}
}
}
But the problem is I'm getting a 500 (Internal Server Error) from grails. The file is not being uploaded.
also getting response as Cannot invoke method isEmpty() on null object
Which means the file has not been sent to the server. Whats the problem here.. Please help..

Try this way. You could create a custom directive for the file upload control
myapp.directive('ngFileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.ngFileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};}])
and in your html use <input type="file" ng-file-model="myFile" />
Then you could create a custom service to do the file upload. Note that its not necessary to create service but it can easily reuse in later file uploads just by injecting the service.
myapp.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl,filename){
var fd = new FormData();
fd.append('file', file);
fd.append('filename', filename);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(data){
console.log(data)
})
.error(function(data){ console.log(data)
});
};}]);
The uploadFileToUrl takes 3 arguments , the file itself , the URL to upload and the file name to be saved as.(You can customize this as you wish) . Use the $http.post to post data.
Finally in your controller , include this
var file = $scope.myFile;
var filename = 'YourFileName'
var uploadUrl = '/UploadLogo/upload' // don't forget to include the leading /
fileUpload.uploadFileToUrl(file, uploadUrl,filename);

Related

How to update data with image using AngularJS $http.put Method

I am created a Laravel application to upload data with image.I am successfully done this task using following way in AngularJs
app.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
app.controller('rowController', function($scope,$http,$modal,Row){
$scope.saveItem=function(){
var fd = new FormData();
//fd.append('photo', $scope.myFile);
for(var key in $scope.newrow)
fd.append(key,$scope.newrow[key]);
$http.post('/api/row_material', fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(data,status,headers,config){
console.log(data);
}).error(function(data,status,headers,config){
console.log(data);
});
}
}
Above POST method successfully worked for me.Now I need to update data with image using $http.put() method.I am created a method as listed below;
$scope.updateItem=function(){
var fd = new FormData();
for(var key in $scope.newrow)
fd.append(key,$scope.newrow[key]);
var uri='api/row_material/1';
$http.put(uri,fd,{headers: '{"Content-Type": undefined}'} )
.success(function (data, status, headers, config) {
alert('Updated Successfully.');
})
.error(function (data, status, header, config) {
alert('Server error');
console.log(data);
});
}
But above put method causes an error given below;
<!DOCTYPE html>
<html>
<head>
<meta name="robots" content="noindex,nofollow" />
<style>
/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
html { background: #eee[…]
Above put method work with out using FormDate() using following way.But Image not uploaded to server
$scope.updateItem=function(){
var uri='api/row_material/1';
$http.put(uri,$scope.newrow,{headers: '{"Content-Type": undefined}'} )
.success(function (data, status, headers, config) {
alert('Updated Successfully.');
})
.error(function (data, status, header, config) {
alert('Server error');
console.log(data);
});
}
I need your help to update data with image using FormData put method in AngularJS
I would suggest you to use ng file upload plugin. I'm not sure what you are using on the backend but this plugin will help you to send file that is selected from input tag with your data. Check all their demo to get a clear idea.
https://github.com/danialfarid/ng-file-upload
Thanks.
There are several good approaches for this..
One of them is the one given below..
var myApp = angular.module('myApp', []);
myApp.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
myApp.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}
}]);
myApp.controller('myCtrl', ['$scope', 'fileUpload', function($scope, fileUpload){
$scope.uploadFile = function(){
var file = $scope.myFile;
console.log('file is ' );
console.dir(file);
var uploadUrl = "/fileUpload";
fileUpload.uploadFileToUrl(file, uploadUrl);
};
}]);
<div ng-app = "myApp" ng-controller = "myCtrl">
<input type = "file" file-model = "myFile"/>
<button ng-click = "uploadFile()">upload me</button>
</div>
This would do for the client side. Now for the server side, you need to write a script that can handle the uploaded image correctly and place it on the destination where you intend to..
This piece from the PHP documentation discusses the PUT method and file uploads (multipart/form-data).
I can't quote any parts from it because I think everything is important. You should read it in detail but to summarize it's not possible without changes to your Apache configuration and creation of auxiliary scripts.
With Laravel you should use [Form Method Spoofing][2] (basically a variable called _method) and keep on using POST. The _method will allow you to call Laravel's PUT action correctly.

Provider 'FileUpload' must return a value from $get factory method

In a web application made with AngularJs there is a page where the user can upload a file. But I have some problem.
This is the Factory that makes the upload:
angular.module('app').factory('FileUpload', ['$http', function($http) {
this.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}
}]);
If I try to upload a file, console gives me this error:
"Error: [$injector:undef] Provider 'FileUpload' must return a value from $get factory method.
This is the function in the Controller:
$scope.uploadFile = function(){
var userId = $stateParams.userId;
var fileType = $stateParams.fileType;
var file = $scope.myFile;
console.log('file is ');
console.dir(file);
var uploadUrl = 'my_url';
FileUpload.uploadFileToUrl(file, uploadUrl);
};
The pattern you're using should use service not factory. With factory you want to return the new'd up instance, with service you just provide the function.
angular.module('app').service('FileUpload',...

AngularJS File upload with directive

I use angularJS directive to get file to upload and I use the directive like this way:
<input type="file" file-model="myFile">
The directive looks like this:
myApp.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
myApp.service('fileUploadService', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl) {
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
console.log('file upload successful');
})
.error(function() {
console.log('file upload error');
});
}
}]);
In controller:
$scope.uploadFile = function() {
var file = $scope.myFile;
if (typeof file != 'undefined' && file != null) {
console.log('file is ' + file);
console.dir(file);
var uploadUrl = "/upload";
fileUpload.uploadFileToUrl(file, uploadUrl);
}
};
The upload works fine. I would have two questions concerning improvements of this upload:
how to upload more than one file - is there a possibility to upload more than one file with this directive
if file was uploaded - how to
how to upload more than one file - is there a possibility to upload more than one file with this directive
You must set multipleon your input field to be able to select multiple files. Then, your file variable will be an array of several files(if several files were chosen) instead of just a File object.
if file was uploaded - how to
... ?

File upload using Angular js and grails

I am trying to implement a file upload using angular js and grails. I am able to call the grails controller method but not able to get the file from angular js using request.getFile() method. Here is my html code,
<script src="angular-file-upload-shim.min.js"></script>
<script src="angular.min.js"></script>
<script src="angular-file-upload.min.js"></script>
<div ng-controller="MyCtrl">
<input type="text" ng-model="myModelObj">
<input type="file" name="file" ng-file-select="onFileSelect($files)" >
<div ng-file-drop="onFileSelect($files)" ng-file-drag-over-class="optional-css-class"
ng-show="dropSupported">drop files here</div>
<div ng-file-drop-available="dropSupported=true"
ng-show="!dropSupported">HTML5 Drop File is not supported!</div>
<button ng-click="upload.abort()">Cancel Upload</button>
</div>
Here is the javascript code,
//inject angular file upload directives and service.
angular.module('myApp', ['angularFileUpload']);
var MyCtrl = [ '$scope', '$upload', function($scope, $upload) {
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
$scope.upload = $upload.upload({
url: 'GrailsApp/upload', //upload.php script, node.js route, or servlet url
data: {myObj: $scope.myModelObj},
file: file, // or list of files: $files for html5 only
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}
};
}];
Here is the grails controller upload method,
def upload() {
def f = request.getFile('file')
if (f.empty) {
flash.message = 'file cannot be empty'
render(view: 'uploadForm')
return
}
f.transferTo(new File('D:/Filestorage/myfile.txt')) response.sendError(200, 'Done') }
While running this code gives a error like below,
Request.getFile() is applicable for argument types: (java.lang.String) values: {"file"}
But definitely it is going inside the controller method. Just not able to get the file from angular js.
But the same code works when I use a gsp form like below,
<g:uploadForm action="upload">
<input type="file" name="file" />
<input type="submit" />
</g:uploadForm>
The same code works like a chram and upload the file successfully.
Docs say the fileName by default is file but add fileFormDataName: "myFile" explicitly as:
$scope.upload = $upload.upload({
url: 'GrailsApp/upload', //upload.php script, node.js route, or servlet url
data: {myObj: $scope.myModelObj},
file: file,
fileFormDataName: "myFile"
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
in angular and then get the file as
request.getFile('myFile')
in Grails side to see if it works.
Also see, instead of explicitly adding a fileFormDataName just rectify that you have removed the trailing , from file: file, and use as earlier, if that helps.
The issue has been fixed. Just need to bind the post with a name, by adding fileFormDataName:"file" in the $scope.upload method.
Here is a sample,
//inject angular file upload directives and service.
angular.module('myApp', ['angularFileUpload']);
var MyCtrl = [ '$scope', '$upload', function($scope, $upload) {
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
$scope.upload = $upload.upload({
url: 'GrailsApp/upload', //upload.php script, node.js route, or servlet url
data: {myObj: $scope.myModelObj},
file: file,
fileFormDataName:'file'
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}
};
}];

angularjs and spring mvc - upload multiple files in one request

I want to implement file uploading in my web application, I am using angular.js on client side and spring mvc on server side.
I managed to get single file upload and multiple file upload working by using https://github.com/danialfarid/angular-file-upload. The thing is, when I upload multiple files each one of them is coming to me as separate request (which is obvious event after reading example code):
//inject angular file upload directives and service.
angular.module('myApp', ['angularFileUpload']);
var MyCtrl = [ '$scope', '$upload', function($scope, $upload) {
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var $file = $files[i];
$scope.upload = $upload.upload({
url: 'server/upload/url', //upload.php script, node.js route, or servlet url
// method: POST or PUT,
// headers: {'headerKey': 'headerValue'}, withCredential: true,
data: {myObj: $scope.myModelObj},
file: $file,
//(optional) set 'Content-Desposition' formData name for file
//fileFormDataName: myFile,
progress: function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
})
//.error(...).then(...);
}
}
}];
there is an iteration through all the files.
Now I am wondering if it is possible to somehow upload multiple files as one, single request.
on spring controller side create
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public String save(#ModelAttribute("filesForm") FileUploadForm filesForm) {
List<MultipartFile> files = filesForm.getFiles();
//do something
}
public class FileUploadForm
{
private List<MultipartFile> files;
// geters and setters ...
}
on client side upload service
return {
send: function(files) {
var data = new FormData(),
xhr = new XMLHttpRequest();
xhr.onloadstart = function() {
console.log('Factory: upload started: ', file.name);
$rootScope.$emit('upload:loadstart', xhr);
};
xhr.onerror = function(e) {
$rootScope.$emit('upload:error', e);
};
xhr.onreadystatechange = function(e)
{
if (xhr.readyState === 4 && xhr.status === 201)
{
$rootScope.$emit('upload:succes',e, xhr, file.name ,file.type);
}
};
angular.forEach(files, function(f) {
data.append('files', f, f.name);
});
xhr.open('POST', '../upload');
xhr.send(data);
}
};

Resources