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

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 at 18:28

14 Answers 14

up vote 180 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
1  
One more solution (IaaS for file uploading): github.com/uploadcare/angular-uploadcare – David Avs Oct 10 '14 at 7:25
11  
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 at 1:33

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 you 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
1  
please explain what is happening here! Whats reader? – Bhumi Singhal Mar 22 '14 at 18:49
5  
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
    
If i need to read an .xls file how can i do it ? – Bhumi Singhal Mar 23 '14 at 8:19
7  
FileReader.readAsBinaryString is deprecated as of 12 July 2012 Working Draft from the W3C. – Shane Stillwell Apr 24 '14 at 13:50
1  
@yagger is there a particular reason why your links are referencing FileReaderSync's readAsArrayBuffer method (which are only available in web workers) instead of the regular, async FileReader API? – doldt Jun 30 at 12:27

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... – RageCompex Aug 16 at 16:06

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

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) {
                    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:

$_FILES['file']
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

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

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

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

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

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

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

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

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.