- AngularJS
Black Sand Solutions
AngularJS File Upload / Download
- AngularJS
A collection of directives and utility methods for working with files in Angular.
Angular File Upload / Download
OnFileChange
It is not possible to bind to input File type in angular so this is not possible: onchange="onFileChange(this)
https://github.com/angular/angular.js/issues/1375
This directive resolves this:
angular.module("directives")
.directive("OnFileChange", [OnFileChange]);
function OnFileChange() {
var directive = {
restrict: "A",
link: function (scope, element, attrs) {
var onChangeHandler = scope.$eval(attrs.OnFileChange);
element.bind('change', onChangeHandler);
element.bind('click', function(){
//clear the value so that we can select a file with the same name
element[0].value = '';
});
scope.$on('$destroy', function () {
element.unbind('change', onChangeHandler);
});
}
}
return directive;
}
ngSrcAuth
This directive allows the img tag to be used to request a file from an API endpoint that requires authentication - in this case bearer. Note that this directive requires the API endpoint to be passed in via the APP_CONFIG
constant. It also expects the logged in users token to be accessible via the sessionService
. Alternatively, both could be passed in scope attributes.
Usage: <img class="thumbnail" ng-src-auth="{{item.apiEndPointPath}}"/>
angular.module('directives')
.directive('ngSrcAuth', ['$http', 'APP_CONFIG', 'sessionService', function ($http, APP_CONFIG, sessionService) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
//request file data from server, attaching auth header and token
var requestConfig = {
method: 'Get',
headers: { 'Authorization': 'Basic ' + sessionService.getToken() },
url: attrs.ngSrcAuth,
cache: 'true'
};
$http(requestConfig)
.success(function (file) {
//append to src attribute on associated img tag
if (file.isPlaceholder)
{
attrs.$set('src', APP_CONFIG.cdnUrl + '/' + file.filepath);
attrs.$set('title', file.filename);
attrs.$set('download-link', file.downloadUrl);
}
else
{
attrs.$set('src', "data:image/jpeg;base64," + file.data);
attrs.$set('title', file.filename);
attrs.$set('download-link', file.downloadUrl);
}
});
}
};
}]);
ngDownloadAuth
Similar to the above this creates a link to a file which will download it from an API endpoint that required authentication. The directive expects sessionService
to return the token for the logged in user.
Supports IE, Firefox and Chrome.
Usage: <a download-link="{{::file.downloadLink}}" target="_blank"> {{::file.filename}}</a>
angular.module('directives')
.directive('downloadLink', ['$http', '$timeout', '$window', 'sessionService', function ($http, $timeout, $window, sessionService) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
attrs.$set('title', 'download');
attrs.$set('style', 'cursor:pointer');
// handle the user clicking the element
element.bind("click", function (e) {
getFile();
});
function getFile() {
//request file data from server, attaching auth header and token
var requestConfig = {
method: 'Get',
headers: { 'Authorization': 'Basic ' + sessionService.getToken() },
url: attrs.downloadLink,
cache: 'true'
};
$http(requestConfig)
.success(function(file) {
//send the file to the browser
download(file.data, file.filename, file.mimeType);
});
}
function download(content, filename, contentType) {
var isChrome = !!window.chrome && !!window.chrome.webstore;
if (isChrome)
{
//create a new a tag and attach the file data to it and trigger the download
//TODO: I am not happy with using the global document, but $document does not have a createElement method and not had time to create a better solution
var link = angular.element(document.createElement('a'));
link.attr('href', 'data:' + contentType + ';base64,' + content);
link.attr('download', filename);
$timeout(function () {
link[0].click();
});
}
else
{
//download is not supported, open file in new window
var blob = b64toBlob(content, contentType);
//IE
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, filename);
}
else
{
//everything else
var url = window.URL || window.webkitURL;
var fileUrl = url.createObjectURL(blob);
$window.open(fileUrl, filename);
url.revokeObjectURL(fileUrl);
}
}
}
function b64toBlob(b64Data, contentType) {
//http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
contentType = contentType || '';
var sliceSize = 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, { type: contentType });
return blob;
}
}
};
}]);
Utility Methods
Some useful utility methods for working with files, primarily images:
GetFileObject
function getFileObject(file) {
///
/// Convert the File object to an image object,
/// complete with size, height and width
///
var deferredObject = $q.defer();
var reader = new FileReader();
//http://stackoverflow.com/questions/12570834/how-to-preview-image-get-file-size-image-height-and-width-before-upload
reader.onloadend = function (_file) {
// This method is called by reader.readAsDataURL when it completes (pass or fail)
var result = {};
var image = new Image();
image.src = _file.target.result;
image.onload = function () {
var w = this.width,
h = this.height,
s = ~~(file.size / 1024) + 'KB';
result.dataUri = _file.target.result;
result.width = w;
result.height = h;
result.size = s;
result.name = file.name;
return deferredObject.resolve(result);
};
image.onerror = function () {
//do nothing
};
}
reader.readAsDataURL(file);
return deferredObject.promise;
}
CreateBlob
The BlobBuilder API has been deprecated in favour of Blob, but older browsers don't know about the Blob constructor
function createBlob(data, datatype) {
var out;
try {
out = new Blob([data], { type: datatype });
}
catch (e) {
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if (e.name == 'TypeError' && window.BlobBuilder) {
var bb = new BlobBuilder();
bb.append(data);
out = bb.getBlob(datatype);
}
else if (e.name == "InvalidStateError") {
out = new Blob([data], { type: datatype });
}
else {
// unknown error
}
}
return out;
}
dataUrItoBlob
Convert base64/URLEncoded data component to raw binary data held in a string. Uses createBlob
method above.
function dataUrItoBlob(dataUri) {
// c
var byteString;
if (dataUri.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataUri.split(',')[1]);
else
byteString = unescape(dataUri.split(',')[1]);
// separate out the mime component
var mimeString = dataUri.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new createBlob(ia, mimeString);
}