ng-file-upload-haikulearning
Version:
An AngularJS directive for file upload using HTML5 with FileAPI polyfill for unsupported browsers
289 lines (243 loc) • 8.82 kB
JavaScript
// customized version of https://github.com/exif-js/exif-js
ngFileUpload.service('UploadExif', ['UploadResize', '$q', function (UploadResize, $q) {
var upload = UploadResize;
upload.isExifSupported = function () {
return window.FileReader && new FileReader().readAsArrayBuffer && upload.isResizeSupported();
};
function applyTransform(ctx, orientation, width, height) {
switch (orientation) {
case 2:
return ctx.transform(-1, 0, 0, 1, width, 0);
case 3:
return ctx.transform(-1, 0, 0, -1, width, height);
case 4:
return ctx.transform(1, 0, 0, -1, 0, height);
case 5:
return ctx.transform(0, 1, 1, 0, 0, 0);
case 6:
return ctx.transform(0, 1, -1, 0, height, 0);
case 7:
return ctx.transform(0, -1, -1, 0, height, width);
case 8:
return ctx.transform(0, -1, 1, 0, 0, width);
}
}
upload.readOrientation = function (file) {
var defer = $q.defer();
var reader = new FileReader();
var slicedFile = file.slice ? file.slice(0, 64 * 1024) : file;
reader.readAsArrayBuffer(slicedFile);
reader.onerror = function (e) {
return defer.reject(e);
};
reader.onload = function (e) {
var result = {orientation: 1};
var view = new DataView(this.result);
if (view.getUint16(0, false) !== 0xFFD8) return defer.resolve(result);
var length = view.byteLength,
offset = 2;
while (offset < length) {
var marker = view.getUint16(offset, false);
offset += 2;
if (marker === 0xFFE1) {
if (view.getUint32(offset += 2, false) !== 0x45786966) return defer.resolve(result);
var little = view.getUint16(offset += 6, false) === 0x4949;
offset += view.getUint32(offset + 4, little);
var tags = view.getUint16(offset, little);
offset += 2;
for (var i = 0; i < tags; i++)
if (view.getUint16(offset + (i * 12), little) === 0x0112) {
var orientation = view.getUint16(offset + (i * 12) + 8, little);
if (orientation >= 2 && orientation <= 8) {
view.setUint16(offset + (i * 12) + 8, 1, little);
result.fixedArrayBuffer = e.target.result;
}
result.orientation = orientation;
return defer.resolve(result);
}
} else if ((marker & 0xFF00) !== 0xFF00) break;
else offset += view.getUint16(offset, false);
}
return defer.resolve(result);
};
return defer.promise;
};
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
upload.applyExifRotation = function (file) {
if (file.type.indexOf('image/jpeg') !== 0) {
return upload.emptyPromise(file);
}
var deferred = $q.defer();
upload.readOrientation(file).then(function (result) {
if (result.orientation < 2 || result.orientation > 8) {
return deferred.resolve(file);
}
upload.dataUrl(file, true).then(function (url) {
var canvas = document.createElement('canvas');
var img = document.createElement('img');
img.onload = function () {
try {
canvas.width = result.orientation > 4 ? img.height : img.width;
canvas.height = result.orientation > 4 ? img.width : img.height;
var ctx = canvas.getContext('2d');
applyTransform(ctx, result.orientation, img.width, img.height);
ctx.drawImage(img, 0, 0);
var dataUrl = canvas.toDataURL(file.type || 'image/WebP', 0.934);
dataUrl = upload.restoreExif(arrayBufferToBase64(result.fixedArrayBuffer), dataUrl);
var blob = upload.dataUrltoBlob(dataUrl, file.name);
deferred.resolve(blob);
} catch (e) {
return deferred.reject(e);
}
};
img.onerror = function () {
deferred.reject();
};
img.src = url;
}, function (e) {
deferred.reject(e);
});
}, function (e) {
deferred.reject(e);
});
return deferred.promise;
};
upload.restoreExif = function (orig, resized) {
var ExifRestorer = {};
ExifRestorer.KEY_STR = 'ABCDEFGHIJKLMNOP' +
'QRSTUVWXYZabcdef' +
'ghijklmnopqrstuv' +
'wxyz0123456789+/' +
'=';
ExifRestorer.encode64 = function (input) {
var output = '',
chr1, chr2, chr3 = '',
enc1, enc2, enc3, enc4 = '',
i = 0;
do {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this.KEY_STR.charAt(enc1) +
this.KEY_STR.charAt(enc2) +
this.KEY_STR.charAt(enc3) +
this.KEY_STR.charAt(enc4);
chr1 = chr2 = chr3 = '';
enc1 = enc2 = enc3 = enc4 = '';
} while (i < input.length);
return output;
};
ExifRestorer.restore = function (origFileBase64, resizedFileBase64) {
if (origFileBase64.match('data:image/jpeg;base64,')) {
origFileBase64 = origFileBase64.replace('data:image/jpeg;base64,', '');
}
var rawImage = this.decode64(origFileBase64);
var segments = this.slice2Segments(rawImage);
var image = this.exifManipulation(resizedFileBase64, segments);
return 'data:image/jpeg;base64,' + this.encode64(image);
};
ExifRestorer.exifManipulation = function (resizedFileBase64, segments) {
var exifArray = this.getExifArray(segments),
newImageArray = this.insertExif(resizedFileBase64, exifArray);
return new Uint8Array(newImageArray);
};
ExifRestorer.getExifArray = function (segments) {
var seg;
for (var x = 0; x < segments.length; x++) {
seg = segments[x];
if (seg[0] === 255 & seg[1] === 225) //(ff e1)
{
return seg;
}
}
return [];
};
ExifRestorer.insertExif = function (resizedFileBase64, exifArray) {
var imageData = resizedFileBase64.replace('data:image/jpeg;base64,', ''),
buf = this.decode64(imageData),
separatePoint = buf.indexOf(255, 3),
mae = buf.slice(0, separatePoint),
ato = buf.slice(separatePoint),
array = mae;
array = array.concat(exifArray);
array = array.concat(ato);
return array;
};
ExifRestorer.slice2Segments = function (rawImageArray) {
var head = 0,
segments = [];
while (1) {
if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 218) {
break;
}
if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 216) {
head += 2;
}
else {
var length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3],
endPoint = head + length + 2,
seg = rawImageArray.slice(head, endPoint);
segments.push(seg);
head = endPoint;
}
if (head > rawImageArray.length) {
break;
}
}
return segments;
};
ExifRestorer.decode64 = function (input) {
var chr1, chr2, chr3 = '',
enc1, enc2, enc3, enc4 = '',
i = 0,
buf = [];
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
var base64test = /[^A-Za-z0-9\+\/\=]/g;
if (base64test.exec(input)) {
console.log('There were invalid base64 characters in the input text.\n' +
'Valid base64 characters are A-Z, a-z, 0-9, ' + ', ' / ',and "="\n' +
'Expect errors in decoding.');
}
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
do {
enc1 = this.KEY_STR.indexOf(input.charAt(i++));
enc2 = this.KEY_STR.indexOf(input.charAt(i++));
enc3 = this.KEY_STR.indexOf(input.charAt(i++));
enc4 = this.KEY_STR.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
buf.push(chr1);
if (enc3 !== 64) {
buf.push(chr2);
}
if (enc4 !== 64) {
buf.push(chr3);
}
chr1 = chr2 = chr3 = '';
enc1 = enc2 = enc3 = enc4 = '';
} while (i < input.length);
return buf;
};
return ExifRestorer.restore(orig, resized); //<= EXIF
};
return upload;
}]);