ng-file-upload (original) (raw)

12.2.13 • Public • Published 9 years ago

npm version Downloads Issue Stats Issue Stats
PayPayl donate button Gratipay donate button

ng-file-upload

Lightweight Angular directive to upload files.

See the DEMO page. Reference docs here

Migration notes: version 3.0.x version 3.1.x version 3.2.x version 4.x.x version 5.x.x version 6.x.x version 6.2.x version 7.0.x version 7.2.x version 8.0.x version 9.0.x version 10.0.x version 11.0.x version 12.0.x version 12.1.x version 12.2.x

Ask questions on StackOverflow under the ng-file-upload tag.
For bug report or feature request please search through existing issues first then open a new one here. For faster response provide steps to reproduce/versions with a jsfiddle link. If you need support for your company contact me.
If you like this plugin give it a thumbs up at ngmodules or get me a cup of tea . Contributions are welcomed.

Table of Content:

Features

Install

 

Usage

Samples:

Upload on form submit or button click

  Single Image with validations

  <div class="button" ngf-select ng-model="file" name="file" ngf-pattern="'image/*'"

    ngf-accept="'image/*'" ngf-max-size="20MB" ngf-min-height="100" 

    ngf-resize="{width: 100, height: 100}">Select

  Multiple files

  Select

  Drop files: Drop

  submit

Upload right away after file selection:

Upload on file select

Upload on file select

  Drop File:

<div ngf-drop="uploadFiles($files)" class="drop-box"

  ngf-drag-over-class="'dragover'" ngf-multiple="true" 

  ngf-pattern="'image/*,application/pdf'">Drop Images or PDFs files here

File Drag/Drop is not supported for this browser

Image thumbnail: 

Audio preview: 

Video preview: 

Javascript code:

var app = angular.module('fileUpload', ['ngFileUpload']);

app.controller('MyCtrl', ['$scope', 'Upload', function ($scope, Upload) {

    $scope.submit = function() {

      if ($scope.form.file.$valid && $scope.file) {

        scope.upload(scope.upload(scope.upload(scope.file);

      }

    };

    $scope.upload = function (file) {

        Upload.upload({

            url: 'upload/url',

            data: {file: file, 'username': $scope.username}

        }).then(function (resp) {

            console.log('Success ' + resp.config.data.file.name + 'uploaded. Response: ' + resp.data);

        }, function (resp) {

            console.log('Error status: ' + resp.status);

        }, function (evt) {

            var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);

            console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);

        });

    };

    $scope.uploadFiles = function (files) {

      if (files && files.length) {

        for (var i = 0; i < files.length; i++) {

          Upload.upload({..., data: {file: files[i]}, ...})...;

        }

        Upload.upload({..., data: {file: files}, ...})...;

      }

    }

}]);

Full reference

File select and drop

At least one of the ngf-select or ngf-drop are mandatory for the plugin to link to the element.ngf-select only attributes are marked with * and ngf-drop only attributes are marked with +.

<div|button|input type="file"|ngf-select|ngf-drop...

  ngf-select="" or "upload($files, ...)" // called when files are selected or cleared

  ngf-drop="" or "upload($files, ...)" // called when files being dropped

    // You can use ng-model or ngf-change instead of specifying function for ngf-drop and ngf-select

    // function parameters are the same as ngf-change

  ngf-change="upload($files, file, file, file, newFiles, duplicateFiles, duplicateFiles, duplicateFiles, invalidFiles, $event)"

    // called when files are selected, dropped, or cleared

  ng-model="myFiles" // binds the valid selected/dropped file or files to the scope model

    // could be an array or single file depending on ngf-multiple and ngf-keep values.

  ngf-model-options="{updateOn: 'change click drop dropUrl paste', allowInvalid: false, debounce: 0}"

    // updateOn could be used to disable resetting on click, or updating on paste, browser image drop, etc. 

    // allowInvalid default is false could allow invalid files in the model

    // debouncing will postpone model update (miliseconds). See angular ng-model-options for more details.

  ngf-model-invalid="invalidFile(s)" // binds the invalid selected/dropped file or files to this model.

  ngf-before-model-change="beforeChange($files, ...)" // called after file select/drop and before 

    // model change, validation and resize is processed

  ng-disabled="boolean" // disables this element

  ngf-select-disabled="boolean" // default false, disables file select on this element

  ngf-drop-disabled="boolean" // default false, disables file drop on this element

  ngf-multiple="boolean" // default false, allows selecting multiple files

  ngf-keep="true|false|'distinct'" // default false, keep the previous ng-model files and 

    // append the new files. "'distinct'" removes duplicate files

    // newFiles and newFiles and newFiles and duplicateFiles are set in ngf-change/select/drop functions.

  ngf-fix-orientation="boolean" //default false, would rotate the jpeg image files that have

    // exif orientation data. See #745. Could be a boolean function like shouldFixOrientation($file) 

    // to decide wethere to fix that file or not.

  *ngf-capture="'camera'" or "'other'" // allows mobile devices to capture using camera

  ngf-accept="'image/'" // standard HTML accept attr, browser specific select popup window

  +ngf-allow-dir="boolean" // default true, allow dropping files only for Chrome webkit browser

  +ngf-include-dir="boolean" //default false, include directories in the dropped file array. 

    //You can detect if they are directory or not by checking the type === 'directory'.

  +ngf-drag-over-class="{pattern: 'image/*', accept:'acceptClass', reject:'rejectClass', delay:100}" 

                    or "'myDragOverClass'" or "calcDragOverClass($event)"

    // default "dragover". drag over css class behaviour. could be a string, a function 

    // returning class name or a json object.

    // accept/reject class only works in Chrome, validating only the file mime type.

    // if pattern is not specified ngf-pattern will be used. See following docs for more info.

  +ngf-drag="drag($isDragging, class, class, class, event)" // function called on drag over/leave events.

    // $isDragging: boolean true if is dragging over(dragover), false if drag has left (dragleave)

    // $class is the class that is being set for the element calculated by ngf-drag-over-class

  +ngf-drop-available="dropSupported" // set the value of scope model to true or false based on file

                                     // drag&drop support for this browser

  +ngf-stop-propagation="boolean" // default false, whether to propagate drag/drop events.

  +ngf-hide-on-drop-not-available="boolean" // default false, hides element if file drag&drop is not

  +ngf-enable-firefox-paste="boolean" // experimental default false, enable firefox image paste by making element contenteditable

  ngf-resize="{width: 100, height: 100, quality: .8, type: 'image/jpeg', 

               ratio: '1:2', centerCrop: true, pattern='.jpg', restoreExif: false}" 

               or resizeOptions() // a function returning a promise which resolves into the options.

    // resizes the image to the given width/height or ratio. Quality is optional between 0.1 and 1.0), 

    // type is optional convert it to the given image type format.

    // centerCrop true will center crop the image if it does not fit within the given width/height or ratio. 

    // centerCrop false (default) will not crop the image and will fit it within the given width/height or ratio 

    // so the resulting image width (or height) could be less than given width (or height).

    // pattern is to resize only the files that their name or type matches the pattern similar to ngf-pattern.

    // restoreExif boolean default true, will restore exif info on the resized image.

  ngf-resize-if="$width > 1000 || height > 1000" or "resizeCondition(height > 1000" or "resizeCondition(height > 1000" or "resizeCondition(file, width, width, width, height)"

    // apply ngf-resize only if this function returns true. To filter specific images to be resized.

  ngf-validate-after-resize="boolean" // default false, if true all validation will be run after 

    // the images are being resized, so any validation error before resize will be ignored.

  //validations:

  ngf-max-files="10" // maximum number of files allowed to be selected or dropped, validate error name: maxFiles

  ngf-pattern="'.pdf,.jpg,video/*,!.jog'" // comma separated wildcard to filter file names and types allowed

              // you can exclude specific files by ! at the beginning.

              // validate error name: pattern

  ngf-min-size, ngf-max-size, ngf-max-total-size="100" in bytes or "'10KB'" or "'10MB'" or "'10GB'"

              // validate as form.file.$error.maxSize=true and file.$error='maxSize'

              // ngf-max-total-size is for multiple file select and validating the total size of all files.

  ngf-min-height, ngf-max-height, ngf-min-width, ngf-max-width="1000" in pixels only images

              // validate error names: minHeight, maxHeight, minWidth, maxWidth

  ngf-ratio="8:10,1.6" // list of comma separated valid aspect ratio of images in float or 2:3 format

              // validate error name: ratio

  ngf-min-ratio, ngf-max-ratio="8:10" // min or max allowed aspect ratio for the image.

  ngf-dimensions="$width > 1000 || height > 1000" or "validateDimension(height > 1000" or "validateDimension(height > 1000" or "validateDimension(file, width, width, width, height)"

              // validate the image dimensions, validate error name: dimensions

  ngf-min-duration, ngf-max-duration="100.5" in seconds or "'10s'" or "'10m'" or "'10h'" only audio, video

              // validate error name: maxDuration

  ngf-duration="$duration > 1000" or "validateDuration($file, $duration)"

              // validate the media duration, validate error name: duration

  ngf-validate="{size: {min: 10, max: '20MB'}, width: {min: 100, max:10000}, height: {min: 100, max: 300}

                ratio: '2x1', duration: {min: '10s', max: '5m'}, pattern: '.jpg'}"

                shorthand form for above validations in one place.

  ngf-validate-fn="validate($file)" // custom validation function, return boolean or string containing the error.

              // validate error name: validateFn

  ngf-validate-async-fn="validate($file)" // custom validation function, return a promise that resolve to

              // boolean or string containing the error. validate error name: validateAsyncFn

  ngf-validate-force="boolean" // default false, if true file.$error will be set if the dimension or duration

              // values for validations cannot be calculated for example image load error or unsupported video by the browser.

              // by default it would assume the file is valid if the duration or dimension cannot be calculated by the browser.

  ngf-ignore-invalid="'pattern maxSize'" // ignore the files that fail the specified validations. They will 

              // just be ignored and will not show up in ngf-model-invalid or make the form invalid.

              // space separated list of validate error names.

  ngf-run-all-validations="boolean" // default false. Runs all the specified validate directives. By default

              // once a validation fails for a file it would stop running other validations for that file.

Upload/Drop

<div|... ngf-no-file-drop>File Drag/drop is not supported

// filter to convert the file to base64 data url.

image

File preview

<img|audio|video|div

  *ngf-src="file" //To preview the selected file, sets src attribute to the file data url.

  *ngf-background="file" //sets background-image style to the file data url.

  ngf-resize="{width: 20, height: 20, quality: 0.9}" // only for image resizes the image before setting it

             // as src or background image. quality is optional.

  ngf-no-object-url="true or false" // see #887 to force base64 url generation instead of object url. Default false

<div|span|...

 *ngf-thumbnail="file" //Generates a thumbnail version of the image file

 ngf-size="{width: 20, height: 20, quality: 0.9}" the image will be resized to this size

        // if not specified will be resized to this element`s client width and height.

 ngf-as-background="boolean" //if true it will set the background image style instead of src attribute.

Upload service:

var upload = Upload.upload({

  *url: 'server/upload/url', 

  *data: {key: file, otherInfo: uploadInfo},

  objectKey: '[k]' or '.k' 

  arrayKey: '[i]' or '[]' or '.i' or '' 

  method: 'POST' or 'PUT'(html5), default POST,

  headers: {'Authorization': 'xxx'}, 

  withCredentials: boolean,

  resumeSizeUrl: '/uploaded/size/url?file=' + file.name 

  resumeSizeResponseReader: function(data) {return data.size;} 

  resumeSize: function() {return promise;} 

  resumeChunkSize: 10000 or '10KB' or '10MB' 

  disableProgress: boolean 

  ... and all other angular $http() options could be used here.

})

upload.then(function(resp) {

  console.log('file ' + resp.config.data.file.name + 'is uploaded successfully. Response: ' + resp.data);

}, function(resp) {

}, function(evt) {

  console.log('progress: ' + parseInt(100.0 * evt.loaded / evt.total) + '% file :'+ evt.config.data.file.name);

});

upload.catch(errorCallback);

upload.finally(callback, notifyCallback);

upload.xhr(function(xhr){

  xhr.upload.addEventListener(...)

});

upload.abort();

Upload.http({

  url: '/server/upload/url',

  headers : {

    'Content-Type': file.type

  },

  data: file

})

Upload.setDefaults({ngfMinSize: 20000, ngfMaxSize:20000000, ...})

Upload.defaults.blobUrlsMaxMemory = 268435456 

Upload.defaults.blobUrlsMaxQueueSize = 200 

Upload.base64DataUrl(files).then(function(urls){...});

Upload.dataUrl(file, boolean).then(function(url){...});

Upload.imageDimensions(file).then(function(dimensions){console.log(dimensions.width, dimensions.height);});

Upload.mediaDuration(file).then(function(durationInSeconds){...});

Upload.resize(file, options).then(function(resizedFile){...});

Upload.isResizeSupported()

Upload.isResumeSupported()

Upload.rename(file, newName)

Upload.jsonBlob(obj)

Upload.json(obj)

var blob = upload.dataUrltoBlob(dataurl, name);

Upload.isUploadInProgress() boolean

Upload.urlToBlob(url).then(function(blob) {...});

Upload.isFile(obj);

Upload.applyExifRotation(file).then(...)

ng-modelThe model value will be a single file instead of an array if all of the followings are true:

validationWhen any of the validation directives specified the form validation will take place and you can access the value of the validation using myForm.myFileInputName.$error.<validate error name>for example form.file.$error.pattern. If multiple file selection is allowed you can specify ngf-model-invalid="invalidFiles" to assing the invalid files to a model and find the error of each individual file with file.$error and description of it with file.$errorParam. You can use angular ngf-model-options to allow invalid files to be set to the ng-model ngf-model-options="{allowInvalid: true}".

Upload multiple files: Only for HTML5 FormData browsers (not IE8-9) you have an array of files or more than one file in your data to send them all in one request . Non-html5 browsers due to flash limitation will upload each file one by one in a separate request. You should iterate over the files and send them one by one for a cross browser solution.

drag and drop styling: For file drag and drop, ngf-drag-over-class could be used to style the drop zone. It can be a function that returns a class name based on the $event. Default is "dragover" string. Only in chrome It could be a json object {accept: 'a', 'reject': 'r', pattern: 'image/*', delay: 10} that specify the class name for the accepted or rejected drag overs. The pattern specified or ngf-pattern will be used to validate the file's mime-typesince that is the only property of the file that is reported by the browser on drag. So you cannot validate the file name/extension, size or other validations on drag. There is also some limitation on some file types which are not reported by Chrome.delay default is 100, and is used to fix css3 transition issues from dragging over/out/over #277.

Upload.setDefaults(): If you have many file selects or drops you can set the default values for the directives by calling Upload.setDefaults(options). options would be a json object with directive names in camelcase and their default values.

**Resumable Uploads:**The plugin supports resumable uploads for large files. On your server you need to keep track of what files are being uploaded and how much of the file is uploaded.

Old browsers

For browsers not supporting HTML5 FormData (IE8, IE9, ...) FileAPI module is used.Note: You need Flash installed on your browser since FileAPI uses Flash to upload files.

These two files FileAPI.min.js, FileAPI.flash.swf will be loaded by the module on demand (no need to be included in the html) if the browser does not supports HTML5 FormData to avoid extra load for HTML5 browsers. You can place these two files beside angular-file-upload-shim(.min).js on your server to be loaded automatically from the same path or you can specify the path to those files if they are in a different path using the following script:

  ...

Old browsers known issues:

Server Side

CORS

To support CORS upload your server needs to allow cross domain requests. You can achieve that by having a filter or interceptor on your upload file server to add CORS headers to the response similar to this: (sample java code)

httpResp.setHeader("Access-Control-Allow-Methods", "POST, PUT, OPTIONS");

httpResp.setHeader("Access-Control-Allow-Origin", "your.other.server.com");

httpResp.setHeader("Access-Control-Allow-Headers", "Content-Type"));

For non-HTML5 IE8-9 browsers you would also need a crossdomain.xml file at the root of your server to allow CORS for flash:(sample xml)

  

  

  

Amazon AWS S3 Upload

For Amazon authentication version 4 see this comment

The demo page has an option to upload to S3. Here is a sample config options:

Upload.upload({

    url: 'https://angular-file-upload.s3.amazonaws.com/', 

    method: 'POST',

    data: {

        key: file.name, 

        AWSAccessKeyId: ,

        acl: 'private', 

        policy: $scope.policy, 

        signature: $scope.signature, 

        "Content-Type": file.type != '' ? file.type : 'application/octet-stream', 

        filename: file.name, 

        file: file

    }

});

This article explains more about these fields and provides instructions on how to generate the policy and signature using a server side tool. These two values are generated from the json policy document which looks like this:

{

    "expiration": "2020-01-01T00:00:00Z",

    "conditions": [

        {"bucket": "angular-file-upload"},

        ["starts-with", "$key", ""],

        {"acl": "private"},

        ["starts-with", "$Content-Type", ""],

        ["starts-with", "$filename", ""],

        ["content-length-range", 0, 524288000]

    ]

}

The demo page provide a helper tool to generate the policy and signature from you from the json policy document. Note: Please use https protocol to access demo page if you are using this tool to generate signature and policy to protect your aws secret key which should never be shared.

Make sure that you provide upload and CORS post to your bucket at AWS -> S3 -> bucket name -> Properties -> Edit bucket policy and Edit CORS Configuration. Samples of these two files:

{

  "Version": "2012-10-17",

  "Statement": [

    {

      "Sid": "UploadFile",

      "Effect": "Allow",

      "Principal": {

        "AWS": "arn:aws:iam::xxxx:user/xxx"

      },

      "Action": [

        "s3:GetObject",

        "s3:PutObject"

      ],

      "Resource": "arn:aws:s3:::angular-file-upload/*"

    },

    {

      "Sid": "crossdomainAccess",

      "Effect": "Allow",

      "Principal": "*",

      "Action": "s3:GetObject",

      "Resource": "arn:aws:s3:::angular-file-upload/crossdomain.xml"

    }

  ]

}

    

        http://angular-file-upload.appspot.com

        POST

        GET

        HEAD

        3000

        *

    

For IE8-9 flash polyfill you need to have a crossdomain.xml file at the root of you S3 bucket. Make sure the content-type of crossdomain.xml is text/xml and you provide read access to this file in your bucket policy.

You can also have a look at https://github.com/nukulb/s3-angular-file-upload for another example with this fix.