Join the Stack Overflow Community
Stack Overflow is a community of 6.4 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

Here is my HTML form:

<form name="myForm" ng-submit="">
    <input ng-model='file' type="file"/>
    <input type="submit" value='Submit'/>
</form>

I want to upload an image from local machine and want to read the content of the uploaded file. All this I want to do using AngularJS.

When I try to print the value of $scope.file it comes as undefined.

share|improve this question
    
what about: stackoverflow.com/questions/12979712/… – Cherniv Sep 2 '13 at 10:32
    
angulartutorial.net/2015/03/… – prash Mar 21 '15 at 18:28

18 Answers 18

up vote 257 down vote accepted

Some of the answers here propose using FormData(), but unfortunately that is a browser object not available in Internet Explorer 9 and below. If you need to support those older browsers, you will need a backup strategy such as using <iframe> or Flash.

There are already many Angular.js modules to perform file uploading. These two have explicit support for older browsers:

And some other options:

One of these should fit your project, or may give you some insight into how to code it yourself.

share|improve this answer
2  
One more solution (IaaS for file uploading): github.com/uploadcare/angular-uploadcare – David Avs Oct 10 '14 at 7:25
19  
EggHead has a good video on this - egghead.io/lessons/angularjs-file-uploads – Adam Zerner Nov 5 '14 at 17:17
    
@fregas, true, but in this case "older" includes IE 9. – Samuel Neff May 26 '15 at 1:33
1  
danialfarid/angular-file-upload is renamed to ng-file-upload – Michael Dec 21 '15 at 10:40
    
I have a solution myself and I shared it at github.com/vikibaarathi/Angular-PHP-Imgur-Upload – VBaarathi Mar 4 at 1:10

The easiest is to use HTML5 API, namely FileReader (http://jsfiddle.net/f8Hee/1/).

HTML is pretty straightforward:

<input type="file" id="file" name="file"/>
<button ng-click="add()">Add</button>

In your controller define 'add' method:

$scope.add = function(){
  var f = document.getElementById('file').files[0],
      r = new FileReader();
  r.onloadend = function(e){
    var data = e.target.result;
    //send your binary data via $http or $resource or do anything else with it
  }
  r.readAsBinaryString(f);
}

Browser Compatibility

Desktop Browsers

Firefox(Gecko) 3.6(1.9.2), Chrome 7, Internet Explorer* 10, Opera* 12.02, Safari 6.0.2

Mobile Browsers

Firefox(Gecko) 32, Chrome 3, Internet Explorer* 10, Opera* 11.5, Safari 6.1

Note : readAsBinaryString() method is deprecated and readAsArrayBuffer() should be used instead.

share|improve this answer
3  
please explain what is happening here! Whats reader? – Bhumi Singhal Mar 22 '14 at 18:49
6  
FileReader is a class from standard HTML5 File API w3.org/TR/FileAPI. It allows you to read data from file specified in html input element and process it inside onloadend callback function. You don't need any library to use this API, its already in your browser (unless you use very old one). Hope this helps. – yagger Mar 23 '14 at 8:18
11  
FileReader.readAsBinaryString is deprecated as of 12 July 2012 Working Draft from the W3C. – Shane Stillwell Apr 24 '14 at 13:50
4  
You should'nt access DOM with angular. Is a very bad practice. – jeanmatthieud Jun 7 '15 at 16:09
2  
@Siderex, not in the controller, but it is totally great to do it from directive. In fact, this is what directives are for. You can read about it in Angular docs docs.angularjs.org/guide/directive – yagger Jun 30 '15 at 12:25

Below is working example of file upload:

http://jsfiddle.net/vishalvasani/4hqVu/

In this one function called

setFiles

From View which will update the file array in controller

or

You can check jQuery File Upload using AngularJS

http://blueimp.github.io/jQuery-File-Upload/angularjs.html

share|improve this answer
    
Hi, i was looking for something through which I can just upload one file and display just below it. However in your example I was not able to do same. Dont mind but I am new to this angularjs and my intention to learn to do this particular objective in a simpler yet robust way. – Aditya Sethi Sep 4 '13 at 14:44
    
This helped a lot. Thanks! – RachelC Dec 5 '13 at 21:03
    
Excellent example without using an additional library / extension. Thanks. – markdsievers Apr 5 '14 at 4:01
4  
Very helpful, just a note.. this uses File API which doesn't work in IE9 or below. – ArjaaAine Jul 18 '14 at 22:21
    
Any idea how I get errors from result? Server might trow an error and I would like to display that error message... – CularBytes Aug 16 '15 at 16:06

This is the 2015 way, without 3rd party libraries. Works on all the latest browsers.

 app.directive('myDirective', function (httpPostFactory) {
    return {
        restrict: 'A',
        scope: true,
        link: function (scope, element, attr) {

            element.bind('change', function () {
                var formData = new FormData();
                formData.append('file', element[0].files[0]);
                httpPostFactory('upload_image.php', formData, function (callback) {
                   // recieve image name to use in a ng-src 
                    console.log(callback);
                });
            });

        }
    };
});

app.factory('httpPostFactory', function ($http) {
    return function (file, data, callback) {
        $http({
            url: file,
            method: "POST",
            data: data,
            headers: {'Content-Type': undefined}
        }).success(function (response) {
            callback(response);
        });
    };
});

HTML:

<input data-my-Directive type="file" name="file">

PHP:

if (isset($_FILES['file']) && $_FILES['file']['error'] == 0) {

// uploads image in the folder images
    $temp = explode(".", $_FILES["file"]["name"]);
    $newfilename = substr(md5(time()), 0, 10) . '.' . end($temp);
    move_uploaded_file($_FILES['file']['tmp_name'], 'images/' . $newfilename);

// give callback to your angular code with the image src name
    echo json_encode($newfilename);
}

js fiddle (only front-end) https://jsfiddle.net/vince123/8d18tsey/31/

share|improve this answer
1  
Doesn't work for me... – Emaborsa Nov 16 '15 at 10:21
    
How would you fetch the file in node? – Juicy Jan 11 at 20:08
    
Any more details? Do you need an ng-submit or a form action? This by itself does nothing – Aron Jan 22 at 13:14
    
@Emaborsa hello I added an jsfiddle and made a more complete php code example. It submits the image after the value of the file input has changed so no ng-submit required. – Vince Verhoeven Jan 23 at 21:45
    
The perfect simplest solution, but it took me ages to work out how to get my WCF services to cope with the data which was being uploaded. It's vital that you take the data stream, and pass it through something like MultiParser to actually read in the file's data: stackoverflow.com/a/23702692/391605 Otherwise you'll be storing raw bytes of "------WebKitFormBoundary Content-Disposition:... etc.." – Mike Gledhill Mar 26 at 6:58

You can achieve nice file and folder upload using flow.js.

https://github.com/flowjs/ng-flow

Check out a demo here

http://flowjs.github.io/ng-flow/

It doesn't support IE7, IE8, IE9, so you'll eventually have to use a compatibility layer

https://github.com/flowjs/fusty-flow.js

share|improve this answer

I tried all alternatives that @Anoyz (Correct answer) gives... and the best solution is https://github.com/danialfarid/angular-file-upload

Some Features:

  • Progress
  • Multifiles
  • Fields
  • Old browsers (IE8-9)

It's work fine for me. You just have to pay attention to instructions.

In server-side i use NodeJs, Express 4 and Multer middleware to manage multipart request.

share|improve this answer
    
How do you show images? From backend, they are successfully going in, but they are getting saved as nlzt9LJWRrAZEO3ZteZUOgGc but without the .png format. How to add that? – Saras Arya Jan 3 at 12:20

  <form ng-controller = "myCtrl">
     <input type = "file" file-model="files" multiple/>
     <button ng-click = "uploadFile()">upload me</button>
     <li ng-repeat="file in files">{{file.name}}</li>
  </form>
  <script src = "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
  <script>
    angular.module('myApp', []).directive('fileModel', ['$parse', function ($parse) {
        return {
           restrict: 'A',
           link: function(scope, element, attrs) {
              element.bind('change', function(){
              $parse(attrs.fileModel).assign(scope,element[0].files)
                 scope.$apply();
              });
           }
        };
     }]).controller('myCtrl', ['$scope', '$http', function($scope, $http){


       $scope.uploadFile=function(){
       var fd=new FormData();
        console.log($scope.files);
        angular.forEach($scope.files,function(file){
        fd.append('file',file);
        });
       $http.post('http://localhost:1337/mediaobject/upload',fd,
                                                            {
                                                            transformRequest: angular.identity,
                                                            headers: {'Content-Type': undefined}
                                                            }).success(function(d){
                                                            console.log(d);
                                                            })


       }
     }]);

  </script>

share|improve this answer
    
Above file will work for multiple file upload – Manoj Ojha Jan 12 at 12:46

Easy with a directive

Html:

<input type="file" file-upload multiple/>

JS:

app.directive('fileUpload', function () {
return {
    scope: true,        //create a new scope
    link: function (scope, el, attrs) {
        el.bind('change', function (event) {
            var files = event.target.files;
            //iterate files since 'multiple' may be specified on the element
            for (var i = 0;i<files.length;i++) {
                //emit event upward
                scope.$emit("fileSelected", { file: files[i] });
            }                                       
        });
    }
};

In the directive we ensure a new scope is created and then listen for changes made to the file input element. When changes are detected with emit an event to all ancestor scopes (upward) with the file object as a parameter.

In your controller:

$scope.files = [];

//listen for the file selected event
$scope.$on("fileSelected", function (event, args) {
    $scope.$apply(function () {            
        //add the file object to the scope's files collection
        $scope.files.push(args.file);
    });
});

Then in your ajax call:

data: { model: $scope.model, files: $scope.files }

http://shazwazza.com/post/uploading-files-and-json-data-in-the-same-request-with-angular-js/

share|improve this answer

i think this is the angular file upload:

ng-file-upload

Lightweight Angular JS directive to upload files.

Here is the DEMO page.Features

  • Supports upload progress, cancel/abort upload while in progress, File drag and drop (html5), Directory drag and drop (webkit), CORS, PUT(html5)/POST methods, validation of file type and size, show preview of selected images/audio/videos.
  • Cross browser file upload and FileReader (HTML5 and non-HTML5) with Flash polyfill FileAPI. Allows client side validation/modification before uploading the file
  • Direct upload to db services CouchDB, imgur, etc... with file's content type using Upload.http(). This enables progress event for angular http POST/PUT requests.
  • Seperate shim file, FileAPI files are loaded on demand for non-HTML5 code meaning no extra load/code if you just need HTML5 support.
  • Lightweight using regular $http to upload (with shim for non-HTML5 browsers) so all angular $http features are available

https://github.com/danialfarid/ng-file-upload

share|improve this answer

You can use a FormData object which is safe and fast:

// Store the file object when input field is changed
$scope.contentChanged = function(event){
    if (!event.files.length)
        return null;

    $scope.content = new FormData();
    $scope.content.append('fileUpload', event.files[0]); 
    $scope.$apply();
}

// Upload the file over HTTP
$scope.upload = function(){
    $http({
        method: 'POST', 
        url: '/remote/url',
        headers: {'Content-Type': undefined },
        data: $scope.content,
    }).success(function(response) {
        // Uploading complete
        console.log('Request finished', response);
    });
}
share|improve this answer
    
Can you please also explain where 'contentChanged' is used exactly? – Marc J. Schmidt Jul 6 at 18:41
    
When a file input changes, triggering this funtion will start upload process. – Farsheed Jul 7 at 0:57
    
Since there's no <input type="file" ng-change="contentChanged($event)">, how to do it? – Marc J. Schmidt Jul 8 at 20:13

Your file and json data uploading at the same time .

 var _post = function (file, jsonData) {
            $http({
                url: your url,
                method: "POST",
                headers: { 'Content-Type': undefined },
                transformRequest: function (data) {
                    var formData = new FormData();
                    formData.append("model", angular.toJson(data.model));
                    formData.append("file", data.files);
                    return formData;
                },
                data: { model: jsonData, files: file }
            }).then(function (response) {
                ;
            });
        }

share|improve this answer
    
great thank you! – Serhat Koroglu Apr 7 at 10:42

Use the onchange event to pass the input file element to your function.

<input type="file" onchange="angular.element(this).scope().fileSelected(this)" />

So when a user selects a file, you have a reference to it without the user needing to click an "Add" or "Upload" button.

$scope.fileSelected = function (element) {
    var myFileSelected = element.files[0];
};
share|improve this answer

You may consider IaaS for file upload, such as Uploadcare. There is an Angular package for it: https://github.com/uploadcare/angular-uploadcare

Technically it's implemented as a directive, providing different options for uploading, and manipulations for uploaded images within the widget:

<uploadcare-widget
  ng-model="object.image.info.uuid"
  data-public-key="YOURKEYHERE"
  data-locale="en"
  data-tabs="file url"
  data-images-only="true"
  data-path-value="true"
  data-preview-step="true"
  data-clearable="true"
  data-multiple="false"
  data-crop="400:200"
  on-upload-complete="onUCUploadComplete(info)"
  on-widget-ready="onUCWidgetReady(widget)"
  value="{{ object.image.info.cdnUrl }}"
 />

More configuration options to play with: https://uploadcare.com/widget/configure/

share|improve this answer

http://jsfiddle.net/vishalvasani/4hqVu/ works fine in chrome and IE (if you update CSS a little in background-image). This is used for updating progress bar:

 scope.progress = Math.round(evt.loaded * 100 / evt.total)

but in FireFox angular's [percent] data is not updated in DOM successfully,although files are uploading successfully.

share|improve this answer
    
For FF you can listen to load event and if the length is computable then fire a progress event to indicate the successful upload. github.com/danialfarid/angular-file-upload already takes care of that. – danial Jan 9 '14 at 7:36
    
It's there, but in given fiddle also it's checked and applied. Still no hope in FF. – mayankcpdixit Jan 9 '14 at 7:41
    
I think if you just call uploadProgress inside uploadComplete it should work for FF – danial Jan 9 '14 at 17:47
    
NO it doesn't, and even if it does can you please explain why? I've give a link to fiddle in my post. If possible can you please update it to working in FF and comment solution's link here? – mayankcpdixit Jan 10 '14 at 5:55
    
What version of Firefox? – danial Jan 10 '14 at 15:43

This should be an update/comment to @jquery-guru's answer but as I don't have enough rep it will go here. It fixes the errors that are now generated by the code.

https://jsfiddle.net/vzhrqotw/

The change is basically:

FileUploadCtrl.$inject = ['$scope']
function FileUploadCtrl(scope) {

To:

app.controller('FileUploadCtrl', function($scope)
{

Feel free to move to a more appropriate location if desired.

share|improve this answer

I've read all the thread and the HTML5 API solution looked the best. But it changes my binary files, corrupting them in a manner I've not investigated. The solution that worked perfectly for me was :

HTML :

<input type="file" id="msds" ng-model="msds" name="msds"/>
<button ng-click="msds_update()">
    Upload
</button>

JS:

msds_update = function() {
    var f = document.getElementById('msds').files[0],
        r = new FileReader();
    r.onloadend = function(e) {
        var data = e.target.result;
        console.log(data);
        var fd = new FormData();
        fd.append('file', data);
        fd.append('file_name', f.name);
        $http.post('server_handler.php', fd, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined}
        })
        .success(function(){
            console.log('success');
        })
        .error(function(){
            console.log('error');
        });
    };
    r.readAsDataURL(f);
}

Server side (PHP):

$file_content = $_POST['file'];
$file_content = substr($file_content,
    strlen('data:text/plain;base64,'));
$file_content = base64_decode($file_content);
share|improve this answer

I know this is a late entry but I have created a simple upload directive. Which you can get working in no time!

<input type="file" multiple ng-simple-upload web-api-url="/api/Upload" callback-fn="myCallback" />

ng-simple-upload more on Github with an example using Web API.

share|improve this answer

Above accepted answer is not browser compatible. If some one has compatibility issue try this.

Fiddle

View Code

 <div ng-controller="MyCtrl">
      <input type="file" id="file" name="file"/>
      <br>
      <button ng-click="add()">Add</button>
      <p>{{data}}</p>
    </div>

Controller code

var myApp = angular.module('myApp',[]);

function MyCtrl($scope) {
    $scope.data = 'none';    
    $scope.add = function(){
      var f = document.getElementById('file').files[0],
          r = new FileReader();
      r.onloadend = function(e){        
          var binary = "";
var bytes = new Uint8Array(e.target.result);
var length = bytes.byteLength;

for (var i = 0; i < length; i++) 
{
    binary += String.fromCharCode(bytes[i]);
}

$scope.data = (binary).toString();

          alert($scope.data);
      }
      r.readAsArrayBuffer(f);
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.