Improved Safari support
Added no-blob IndexedDB support. Disabled for Safari, because IDB is very slow here. Added FileManager.getFileCorrectUrl method for Safari
This commit is contained in:
parent
78525c373d
commit
10c6d37656
@ -1233,17 +1233,7 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
|
||||
if (src.substr(0, 5) == 'data:') {
|
||||
remove = true;
|
||||
src = src.substr(5).split(';');
|
||||
var contentType = src[0];
|
||||
var base64 = atob(src[1].split(',')[1]);
|
||||
var array = new Uint8Array(base64.length);
|
||||
|
||||
for (var i = 0; i < base64.length; i++) {
|
||||
array[i] = base64.charCodeAt(i);
|
||||
}
|
||||
|
||||
var blob = new Blob([array], {type: contentType});
|
||||
|
||||
var blob = dataUrlToBlob(src);
|
||||
ErrorService.confirm({type: 'FILE_CLIPBOARD_PASTE'}).then(function () {
|
||||
$scope.draftMessage.files = [blob];
|
||||
$scope.draftMessage.isMedia = true;
|
||||
|
@ -92,6 +92,52 @@ function uint6ToBase64 (nUint6) {
|
||||
: 65;
|
||||
}
|
||||
|
||||
function base64ToBlob(base64str, mimeType) {
|
||||
var sliceSize = 1024;
|
||||
var byteCharacters = atob(base64str);
|
||||
var bytesLength = byteCharacters.length;
|
||||
var slicesCount = Math.ceil(bytesLength / sliceSize);
|
||||
var byteArrays = new Array(slicesCount);
|
||||
|
||||
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
|
||||
var begin = sliceIndex * sliceSize;
|
||||
var end = Math.min(begin + sliceSize, bytesLength);
|
||||
|
||||
var bytes = new Array(end - begin);
|
||||
for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) {
|
||||
bytes[i] = byteCharacters[offset].charCodeAt(0);
|
||||
}
|
||||
byteArrays[sliceIndex] = new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
return blobConstruct(byteArrays, mimeType);
|
||||
}
|
||||
|
||||
function dataUrlToBlob(url) {
|
||||
// var name = 'b64blob ' + url.length;
|
||||
// console.time(name);
|
||||
var urlParts = url.split(',');
|
||||
var base64str = urlParts[1];
|
||||
var mimeType = urlParts[0].split(':')[1].split(';')[0];
|
||||
var blob = base64ToBlob(base64str, mimeType);
|
||||
// console.timeEnd(name);
|
||||
return blob;
|
||||
}
|
||||
|
||||
function blobConstruct (blobParts, mimeType) {
|
||||
var blob;
|
||||
try {
|
||||
blob = new Blob(blobParts, {type: mimeType});
|
||||
} catch (e) {
|
||||
var bb = new BlobBuilder;
|
||||
angular.forEach(blobParts, function(blobPart) {
|
||||
bb.append(blobPart);
|
||||
});
|
||||
blob = bb.getBlob(mimeType);
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
|
||||
function bytesCmp (bytes1, bytes2) {
|
||||
var len = bytes1.length;
|
||||
if (len != bytes2.length) {
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
|
||||
|
||||
.factory('MtpApiManager', function (Storage, MtpAuthorizer, MtpNetworkerFactory, MtpSingleInstanceService, ErrorService, $q) {
|
||||
.factory('MtpApiManager', function (Storage, MtpAuthorizer, MtpNetworkerFactory, MtpSingleInstanceService, ErrorService, qSync, $q) {
|
||||
var cachedNetworkers = {},
|
||||
cachedUploadNetworkers = {},
|
||||
cachedExportPromise = {},
|
||||
@ -65,9 +65,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
|
||||
}
|
||||
|
||||
if (cache[dcID] !== undefined) {
|
||||
return {then: function (cb) {
|
||||
cb(cache[dcID]);
|
||||
}};
|
||||
return qSync.when(cache[dcID]);
|
||||
}
|
||||
|
||||
var akk = 'dc' + dcID + '_auth_key',
|
||||
|
@ -33,10 +33,23 @@ angular.module('izhukov.utils', [])
|
||||
|
||||
})
|
||||
|
||||
.service('FileManager', function ($window, $q, $timeout) {
|
||||
.service('qSync', function () {
|
||||
|
||||
return {
|
||||
when: function (result) {
|
||||
return {then: function (cb) {
|
||||
return cb(result);
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
.service('FileManager', function ($window, $q, $timeout, qSync) {
|
||||
|
||||
$window.URL = $window.URL || $window.webkitURL;
|
||||
$window.BlobBuilder = $window.BlobBuilder || $window.WebKitBlobBuilder || $window.MozBlobBuilder;
|
||||
var buggyUnknownBlob = navigator.userAgent.indexOf('Safari') != -1;
|
||||
|
||||
var blobSupported = true;
|
||||
|
||||
@ -61,20 +74,6 @@ angular.module('izhukov.utils', [])
|
||||
});
|
||||
}
|
||||
|
||||
function blobConstruct (blobParts, mimeType) {
|
||||
var blob;
|
||||
try {
|
||||
blob = new Blob(blobParts, {type: mimeType});
|
||||
} catch (e) {
|
||||
var bb = new BlobBuilder;
|
||||
angular.forEach(blobParts, function(blobPart) {
|
||||
bb.append(blobPart);
|
||||
});
|
||||
blob = bb.getBlob(mimeType);
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
|
||||
function fileWriteData(fileWriter, bytes) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
@ -182,42 +181,68 @@ angular.module('izhukov.utils', [])
|
||||
return 'data:' + mimeType + ';base64,' + bytesToBase64(fileData);
|
||||
}
|
||||
|
||||
function getDataUrl(blob) {
|
||||
var deferred;
|
||||
try {
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function() {
|
||||
deferred.resolve(reader.result);
|
||||
}
|
||||
reader.readAsDataURL(blob);
|
||||
} catch (e) {
|
||||
return $q.reject(e);
|
||||
}
|
||||
|
||||
deferred = $q.defer();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getFileCorrectUrl(blob, mimeType) {
|
||||
if (buggyUnknownBlob) {
|
||||
var mimeType = blob.type || blob.mimeType || mimeType || '';
|
||||
if (!mimeType.match(/image\/(jpeg|gif|png|bmp)|video\/quicktime/)) {
|
||||
return getDataUrl(blob);
|
||||
}
|
||||
}
|
||||
return qSync.when(getUrl(blob, mimeType));
|
||||
}
|
||||
|
||||
function downloadFile (blob, mimeType, fileName) {
|
||||
if (window.navigator && navigator.msSaveBlob !== undefined) {
|
||||
window.navigator.msSaveBlob(blob, fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
var url = getUrl(blob, mimeType);
|
||||
|
||||
var anchor = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
|
||||
anchor.href = url;
|
||||
anchor.target = '_blank';
|
||||
anchor.download = fileName;
|
||||
if (anchor.dataset) {
|
||||
anchor.dataset.downloadurl = ["video/quicktime", fileName, url].join(':');
|
||||
}
|
||||
$(anchor).css({position: 'absolute', top: 1, left: 1}).appendTo('body');
|
||||
|
||||
try {
|
||||
var clickEvent = document.createEvent('MouseEvents');
|
||||
clickEvent.initMouseEvent(
|
||||
'click', true, false, window, 0, 0, 0, 0, 0
|
||||
, false, false, false, false, 0, null
|
||||
);
|
||||
anchor.dispatchEvent(clickEvent);
|
||||
} catch (e) {
|
||||
console.error('Download click error', e);
|
||||
try {
|
||||
console.error('Download click error', e);
|
||||
anchor[0].click();
|
||||
} catch (e) {
|
||||
window.open(url, '_blank');
|
||||
getFileCorrectUrl(blob, mimeType).then(function (url) {
|
||||
var anchor = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
|
||||
anchor.href = url;
|
||||
anchor.target = '_blank';
|
||||
anchor.download = fileName;
|
||||
if (anchor.dataset) {
|
||||
anchor.dataset.downloadurl = ["video/quicktime", fileName, url].join(':');
|
||||
}
|
||||
}
|
||||
$timeout(function () {
|
||||
$(anchor).remove();
|
||||
}, 100);
|
||||
$(anchor).css({position: 'absolute', top: 1, left: 1}).appendTo('body');
|
||||
|
||||
try {
|
||||
var clickEvent = document.createEvent('MouseEvents');
|
||||
clickEvent.initMouseEvent(
|
||||
'click', true, false, window, 0, 0, 0, 0, 0
|
||||
, false, false, false, false, 0, null
|
||||
);
|
||||
anchor.dispatchEvent(clickEvent);
|
||||
} catch (e) {
|
||||
console.error('Download click error', e);
|
||||
try {
|
||||
console.error('Download click error', e);
|
||||
anchor[0].click();
|
||||
} catch (e) {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}
|
||||
$timeout(function () {
|
||||
$(anchor).remove();
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
@ -228,6 +253,8 @@ angular.module('izhukov.utils', [])
|
||||
getFakeFileWriter: getFakeFileWriter,
|
||||
chooseSave: chooseSaveFile,
|
||||
getUrl: getUrl,
|
||||
getDataUrl: getDataUrl,
|
||||
getFileCorrectUrl: getFileCorrectUrl,
|
||||
download: downloadFile
|
||||
};
|
||||
})
|
||||
@ -237,11 +264,15 @@ angular.module('izhukov.utils', [])
|
||||
$window.indexedDB = $window.indexedDB || $window.webkitIndexedDB || $window.mozIndexedDB || $window.OIndexedDB || $window.msIndexedDB;
|
||||
$window.IDBTransaction = $window.IDBTransaction || $window.webkitIDBTransaction || $window.OIDBTransaction || $window.msIDBTransaction;
|
||||
|
||||
var dbName = 'cachedFiles',
|
||||
dbStoreName = 'files',
|
||||
dbVersion = 1,
|
||||
openDbPromise,
|
||||
storageIsAvailable = $window.indexedDB !== undefined && $window.IDBTransaction !== undefined;
|
||||
var dbName = 'cachedFiles';
|
||||
var dbStoreName = 'files';
|
||||
var dbVersion = 1;
|
||||
var openDbPromise;
|
||||
var storageIsAvailable = $window.indexedDB !== undefined &&
|
||||
$window.IDBTransaction !== undefined &&
|
||||
navigator.userAgent.indexOf('Safari') == -1;
|
||||
// As of Safari 8.0 IndexedDB is REALLY slow, no point in it
|
||||
var storeBlobsAvailable = storageIsAvailable || false;
|
||||
|
||||
function isAvailable () {
|
||||
return storageIsAvailable;
|
||||
@ -307,15 +338,24 @@ angular.module('izhukov.utils', [])
|
||||
|
||||
function saveFile (fileName, blob) {
|
||||
return openDatabase().then(function (db) {
|
||||
if (!storeBlobsAvailable) {
|
||||
return saveFileBase64(db, fileName, blob);
|
||||
}
|
||||
|
||||
try {
|
||||
var deferred = $q.defer(),
|
||||
objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName),
|
||||
var objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName),
|
||||
request = objectStore.put(blob, fileName);
|
||||
} catch (error) {
|
||||
if (storeBlobsAvailable) {
|
||||
storeBlobsAvailable = false;
|
||||
return saveFileBase64(db, fileName, blob);
|
||||
}
|
||||
storageIsAvailable = false;
|
||||
return $q.reject(error);
|
||||
}
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
request.onsuccess = function (event) {
|
||||
deferred.resolve(blob);
|
||||
};
|
||||
@ -328,6 +368,38 @@ angular.module('izhukov.utils', [])
|
||||
});
|
||||
};
|
||||
|
||||
function saveFileBase64(db, fileName, blob) {
|
||||
try {
|
||||
var reader = new FileReader();
|
||||
reader.readAsDataURL(blob);
|
||||
} catch (e) {
|
||||
storageIsAvailable = false;
|
||||
return $q.reject();
|
||||
}
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
reader.onloadend = function() {
|
||||
try {
|
||||
var objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName),
|
||||
request = objectStore.put(reader.result, fileName);
|
||||
} catch (error) {
|
||||
storageIsAvailable = false;
|
||||
deferred.reject(error);
|
||||
return;
|
||||
};
|
||||
request.onsuccess = function (event) {
|
||||
deferred.resolve(blob);
|
||||
};
|
||||
|
||||
request.onerror = function (error) {
|
||||
deferred.reject(error);
|
||||
};
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getFile (fileName) {
|
||||
return openDatabase().then(function (db) {
|
||||
var deferred = $q.defer(),
|
||||
@ -335,10 +407,14 @@ angular.module('izhukov.utils', [])
|
||||
request = objectStore.get(fileName);
|
||||
|
||||
request.onsuccess = function (event) {
|
||||
if (event.target.result === undefined) {
|
||||
var result = event.target.result;
|
||||
if (result === undefined) {
|
||||
deferred.reject();
|
||||
} else if (typeof result === 'string' &&
|
||||
result.substr(0, 5) === 'data:') {
|
||||
deferred.resolve(dataUrlToBlob(result));
|
||||
} else {
|
||||
deferred.resolve(event.target.result);
|
||||
deferred.resolve(result);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2500,9 +2500,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
});
|
||||
|
||||
downloadPromise.then(function (blob) {
|
||||
var url = FileManager.getUrl(blob, mimeType);
|
||||
FileManager.getFileCorrectUrl(blob, mimeType).then(function (url) {
|
||||
historyVideo.url = $sce.trustAsResourceUrl(url);
|
||||
});
|
||||
|
||||
delete historyVideo.progress;
|
||||
historyVideo.url = $sce.trustAsResourceUrl(url);
|
||||
historyVideo.downloaded = true;
|
||||
console.log('video save done');
|
||||
}, function (e) {
|
||||
@ -2655,9 +2657,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
});
|
||||
|
||||
downloadPromise.then(function (blob) {
|
||||
var url = FileManager.getUrl(blob, doc.mime_type);
|
||||
FileManager.getFileCorrectUrl(blob, doc.mime_type).then(function (url) {
|
||||
historyDoc.url = $sce.trustAsResourceUrl(url);
|
||||
})
|
||||
delete historyDoc.progress;
|
||||
historyDoc.url = $sce.trustAsResourceUrl(url);
|
||||
historyDoc.downloaded = true;
|
||||
console.log('file save done');
|
||||
}, function (e) {
|
||||
@ -2771,9 +2774,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
});
|
||||
|
||||
downloadPromise.then(function (blob) {
|
||||
var url = FileManager.getUrl(blob, mimeType);
|
||||
FileManager.getFileCorrectUrl(blob, mimeType).then(function (url) {
|
||||
historyAudio.url = $sce.trustAsResourceUrl(url);
|
||||
});
|
||||
delete historyAudio.progress;
|
||||
historyAudio.url = $sce.trustAsResourceUrl(url);
|
||||
historyAudio.downloaded = true;
|
||||
console.log('audio save done');
|
||||
}, function (e) {
|
||||
|
Loading…
Reference in New Issue
Block a user