(function () { 'use strict'; //service that handles uploading of files and images var serviceId = 'fileUpload'; angular.module('app').factory(serviceId, [ 'config', '$q', 'Dropbox', '$upload','user', fileUpload]); function fileUpload( config, $q, Dropbox, $upload,user) { var service = { uploadFile: uploadFile, uploadImage: uploadImage, uploadVideo:uploadVideo, getOpenBadgeData: getOpenBadgeData }; return service; //get the OpenBadge metadata from a PNG image file function getOpenBadgeData(fileContents, fileType) { if (!fileType) fileType = 'png'; //only works for PNG files if (fileType === 'png') { //this is a bit hacky, it volves searching for standard chunks in the metadata and grabbing the badge data json which should be there var startMetadata = fileContents.indexOf('IHDR'); var possibleStartMetadaChunks = ['ctEXt']; var possibleEndMetadaChunks = ['PLTE', 'IDAT', 'cHRM', 'gAMA', 'iCCP', 'sBIT', 'sRGB', 'bKGD', 'hIST', 'tRNS', 'pHYs', 'sPLT', 'IEND']; var lowestEndMetadataIndex = fileContents.length - 1; var lowestStartMetadataIndex = fileContents.length - 1; for (var i = 0; i < possibleEndMetadaChunks.length; i++) { var index = fileContents.indexOf(possibleEndMetadaChunks[i]); if (index > -1 && index < lowestEndMetadataIndex) lowestEndMetadataIndex = index; } for (var i = 0; i < possibleStartMetadaChunks.length; i++) { var index = fileContents.indexOf(possibleStartMetadaChunks[i]); if (index > -1 && index < lowestStartMetadataIndex) { lowestStartMetadataIndex = index; } } var metadata = fileContents.substring(startMetadata, lowestEndMetadataIndex); var metadatahigh = fileContents.substring(lowestEndMetadataIndex, fileContents.length - 15); if (metadata.indexOf('openbadges') === -1) { //its not an open badge file var alternateBadge = true; if (metadatahigh.indexOf('openbadges') === -1) { return null; } } if (alternateBadge) { return metadatahigh; } else { var startOfJson = metadata.indexOf('{'); var endOfJson = metadata.lastIndexOf('}') + 1; var json = metadata.substring(startOfJson, endOfJson); return JSON.parse(json); } } } //get the url of the Azure blob bucket to upload files to function getBucketUploadUrl(fileName) { fileName = fileName.replace("#", ""); return config.azureBlobUrl + config.uploadBucketName + '/' + fileName; } //upload an image function uploadImage(obj, file, success, progress, error, uploadPath) { user.getProfile().then(function (theUser) { var data = { upload_preset: config.cloudinaryUploadPreset, tags: 'myimages,' + theUser.id }; //images are uploaded to Cloudinary $upload.upload({ url: config.cloudinaryApiBaseUrl + config.cloudinaryName + uploadPath, data: data, file: file }).progress(function(e) { var percent = Math.round((e.loaded * 100.0) / e.total); return progress(percent); }).success(function(data, status, headers, config) { return success(obj, data); }).error(function(data) { return error(); }); }); } //upload a video function uploadVideo(obj, file, success, progress, error, uploadPath) { user.getProfile().then(function (theUser) { var data = { upload_preset: config.cloudinaryVideoUploadPreset, tags: 'myvideos,' + theUser.id }; //videos are uploaded to Cloudinary $upload.upload({ url: config.cloudinaryApiBaseUrl + config.cloudinaryName + uploadPath, data: data, file: file }).progress(function (e) { var percent = Math.round((e.loaded * 100.0) / e.total); return progress(percent); }).success(function (data, status, headers, config) { return success(obj, data); }).error(function (data) { return error(); }); }); } //upload a (non-image) file function uploadFile(fileItem, file, success, progress, error) { user.getProfile().then(function(theUser) { if (theUser.isDropboxDefault) //upload to dropbox return uploadToDropbox(fileItem, file, success, progress, error); else //upload to azure blob storage return uploadToBlob(fileItem, file, success, progress, error); }); } //we use Azure blob storage to upload files to - get the Azure Blob url for the file function getBlobUploadUrl(fileName) { //get the base url of blob storage var baseUrl = getBucketUploadUrl(fileName); //get the SAS (Secure Access Signature) token from cache (this signature allows us write access permissions to Azure Blob storage - so we can write the file there) var sas = user.getUploadBucketSasCached(); if (sas) { //check if the SAS token is still valid, if it is use the token - using moment.js for easy calculation of times var expiry = moment(sas.expiryTime).subtract('seconds', '30'); //30 seconds to allow for small differences in time calculations between machines if (expiry.isAfter(moment())) { var deferred = $q.defer(); deferred.resolve({ baseUrl: baseUrl, fullUrl: baseUrl + sas.sas }); return deferred.promise; } } //we don't have a valid existing SAS so get the sas from the server return user.getUploadBucketSas().then(function (sas) { return { baseUrl: baseUrl, fullUrl: baseUrl + sas.sas }; }); } //upload the file to blob storage (Azure) function uploadToBlob(fileItem, file, success, progress, error) { //first get the upload URL getBlobUploadUrl(fileItem.fileName).then(function (urls) { var fileReader = new FileReader(); fileItem.fileUrl = urls.baseUrl; fileItem.isDropBoxFile = false; fileReader.readAsArrayBuffer(file); //use the $upload service to write the file as part of an HTTP PUT request to Azure Blob storage fileReader.onload = function (e) { $upload.http({ url: urls.fullUrl, method: 'PUT', headers: { //azure needs these extra headers 'x-ms-blob-type': 'BlockBlob', 'Content-Type': file.type }, data: e.target.result }).progress(function(e) { //inform the user what progress we are making on the upload var percent = Math.round((e.loaded * 100.0) / e.total); return progress(percent); }).success(function(data, status, headers, config) { return success(fileItem,data); }).error(function(data) { return error(); }); } }); } //upload the file to Dropbox function uploadToDropbox(fileItem, file, success, progress, error) { //first get the dropbox oauth access token Dropbox.getAccessToken().then(function (dropboxOauth) { var fileReader = new FileReader(); fileItem.isDropBoxFile = true; fileReader.readAsArrayBuffer(file); fileReader.onload = function(e) { $upload.http({ url: Dropbox.urls.putFile + fileItem.fileName, method: 'PUT', headers: { 'Content-Type': fileItem.fileType, 'Content-Length': fileItem.fileSize, 'Authorization': 'Bearer ' + dropboxOauth.access_token //supply the oauth token in the header }, data: e.target.result }).progress(function(e) { var percent = Math.round((e.loaded * 100.0) / e.total); return progress(percent); }).success(function (data, status, headers, config) { fileItem.fileUrl = data.path; return success(fileItem); }).error(function(data) { return error(); }); } }); } } })();