(function () { 'use strict'; var app = angular.module('app'); // Service that handles uploading of files and images - using old Config var serviceId = 'fileUpload'; 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 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(); }); } }); } } // Promise based service for handling file and image uploads var serviceId = 'fileUploadNew'; app.factory(serviceId, ['config', 'cloudinaryConfig', 'userService', '$upload', 'Dropbox', fileUploadNew]); function fileUploadNew(config, cloudinaryConfig, userService, $upload, Dropbox) { var service = {}; // Calculate the progress of a file upload function calculateUploadProgress(event) { return Math.round((event.loaded * 100.0) / event.total); } // Get the Azure Blob url for the file function getBlobUploadUrl(fileName) { fileName = fileName.replace("#", ""); var baseUrl = config.azureBlobUrl + config.uploadBucketName + '/' + fileName; return userService.getUploadBucketSas().then(function (token) { return { baseUrl: baseUrl, fullUrl: baseUrl + token.sas }; }); } // Upload an image service.uploadImage = function (file, progress) { return userService.profile(function (user) { var data = { upload_preset: cloudinaryConfig.uploadPreset, tags: 'myimages,' + user.id }; return $upload.upload({ url: cloudinaryConfig.apiBaseUrl + cloudinaryConfig.name + cloudinaryConfig.uploadPath, data: data, file: file }).progress(function (e) { return progress(calculateUploadProgress(e)); }); }); } // Upload a video service.uploadVideo = function (file, progress) { return userService.profile(function (user) { var data = { upload_preset: cloudinaryConfig.videoUploadPreset, tags: 'myvideos,' + user.id }; return $upload.upload({ url: cloudinaryConfig.apiBaseUrl + cloudinaryConfig.name + cloudinaryConfig.uploadPath, data: data, file: file }).progress(function (e) { return progress(calculateUploadProgress(e)); }); }); } // Upload a non image file service.uploadFile = function (fileItem, file, progress) { return userService.profile(function (user) { return user.isDropboxDefault ? uploadToDropbox(fileItem, file, progress) : uploadToBlob(fileItem, file, progress); }); } // Upload the file to blob storage (Azure) function uploadToBlob(fileItem, file, progress) { return getBlobUploadUrl(fileItem.fileName).then(function (urls) { return new Promise(function (resolve, reject) { var fileReader = new FileReader(); fileItem.fileUrl = urls.baseUrl; fileItem.isDropBoxFile = false; fileReader.readAsArrayBuffer(file); 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) { return progress(calculateUploadProgress(e)); }).success(function (data) { resolve(data); }).error(function () { reject(); }); } }); }); } // Upload the file to dropbox function uploadToDropbox(fileItem, file, progress) { return Dropbox.getAccessToken().then(function (dropboxOauth) { return new Promise(function (resolve, reject) { 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': file.type, 'Content-Length': file.size, 'Authorization': 'Bearer ' + dropboxOauth.access_token //supply the oauth token in the header }, data: e.target.result }).progress(function (e) { return progress(calculateUploadProgress(e)); }).success(function (data) { resolve(data); }).error(function () { reject(); }); } }); }); } return service; } })();