Fixed large file upload to device on mobile site.
Ylian Saint-Hilaire committed
Dec 16, 2021 at 16:28 UTC
7333c0065053f8741971a09ee9ea4eced4edc1e3
1 file changed
+58
-17
views/default-mobile.handlebars
+58
-17
@@ -5418,6 +5418,19 @@
5418
p13uploadNextFile();
5419
}
5420
5421
+ // Perform SHA-384 hashing
5422
+ const byteToHex = [];
5423
+ for (var n = 0; n <= 0xff; ++n) { var hexOctet = n.toString(16).padStart(2, '0'); byteToHex.push(hexOctet); }
5424
+ function arrayBufferToHex(arrayBuffer) { return Array.prototype.map.call(new Uint8Array(arrayBuffer), n => byteToHex[n]).join(''); }
5425
+ function performHash(data, f) { window.crypto.subtle.digest('SHA-384', data).then(function (v) { f(arrayBufferToHex(v)); }, function () { f(null); }); }
5426
+ function performHashOnFile(file, f) {
5427
+ // TODO: At some point, try to make this work for files of unlimited size using a digest stream
5428
+ var reader = new FileReader();
5429
+ reader.onerror = function (err) { f(null); }
5430
+ reader.onload = function () { window.crypto.subtle.digest('SHA-384', reader.result).then(function (v) { f(arrayBufferToHex(v)); }, function () { f(null); }); };
5431
+ reader.readAsArrayBuffer(file);
5432
+ }
5433
+
5434
// Push the next file
5435
function p13uploadNextFile() {
5436
uploadFile.xfilePtr++;
@@ -5428,13 +5441,15 @@
5441
Q('d2progressBar').max = file.size;
5442
Q('d2progressBar').value = 0;
5443
if (file.xdata == null) {
5431
- // Load the data
5432
- uploadFile.xreader = new FileReader();
5433
- uploadFile.xreader.onload = function () {
5434
- uploadFile.xdata = uploadFile.xreader.result;
5435
- files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }));
5436
- };
5437
- uploadFile.xreader.readAsArrayBuffer(file);
5444
+ uploadFile.xfile = file;
5445
+ // If the remote file already exists and is smaller then our file, see if we can resume the trasfer
5446
+ var f = null;
5447
+ for (var i in p13filetree.dir) { if (p13filetree.dir[i].n == file.name) { f = p13filetree.dir[i]; } }
5448
+ if ((f != null) && (f.s <= uploadFile.xfile.size)) {
5449
+ performHashOnFile(uploadFile.xfile, function (hash) { files.sendText(JSON.stringify({ action: 'uploadhash', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, tag: { h: hash.toUpperCase(), s: f.s, skip: f.s == uploadFile.xfile.size } })); });
5450
+ } else {
5451
+ files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xfile.size }));
5452
+ }
5453
} else {
5454
// Data already loaded
5455
uploadFile.xdata = file.xdata;
@@ -5462,24 +5477,44 @@
5477
function p13gotUploadData(cmd) {
5478
if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
5479
switch (cmd.action) {
5465
- case 'uploadstart': { p13uploadNextPart(false); for (var i = 0; i < 8; i++) { p13uploadNextPart(true); } break; } // Send 8 more blocks of 16k to fill the websocket.
5480
+ case 'uploadstart': { uploadFile.xdataPriming = 8; p13uploadNextPart(false); break; } // Send 8 more blocks of 16k to fill the websocket.
5481
case 'uploadack': { p13uploadNextPart(false); break; }
5482
case 'uploaddone': { if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadNextFile(); } else { p13uploadFileTransferDone(); } break; }
5483
case 'uploaderror': { p13uploadFileCancel(); break; }
5484
+ case 'uploadhash': {
5485
+ var file = uploadFile.xfiles[uploadFile.xfilePtr];
5486
+ if (file) {
5487
+ if (cmd.tag.h === cmd.hash) {
5488
+ if (cmd.tag.skip) {
5489
+ p13uploadNextFile();
5490
+ } else {
5491
+ uploadFile.xptr = cmd.tag.s;
5492
+ files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xfile.size, append: true }));
5493
+ }
5494
+ } else {
5495
+ files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xfile.size, append: false }));
5496
+ }
5497
+ }
5498
+ break;
5499
+ }
5500
}
5501
}
5502
5503
// Push the next part of the file into the websocket. If dataPriming is true, push more data only if it's not the last block of the file.
5504
function p13uploadNextPart(dataPriming) {
5474
- var data = uploadFile.xdata, start = uploadFile.xptr;
5475
- if (start >= data.byteLength) {
5476
- files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr }));
5477
- } else {
5478
- var end = uploadFile.xptr + 16384;
5479
- if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; }
5480
- var dataslice = new Uint8Array(data.slice(start, end))
5505
+ if (uploadFile.xreader != null) return; // Data reading already in process
5506
+ if (uploadFile.xptr >= uploadFile.xfile.size) return;
5507
+ var end = uploadFile.xptr + 16384;
5508
+ if (end > uploadFile.xfile.size) { if (dataPriming == true) { return; } end = uploadFile.xfile.size; }
5509
+ uploadFile.xreader = new FileReader();
5510
+ uploadFile.xreader.onerror = function (err) { console.log(err); }
5511
+ uploadFile.xreader.onload = function () {
5512
+ var data = uploadFile.xreader.result;
5513
+ delete uploadFile.xreader;
5514
+ if (data == null) return;
5515
+ var dataslice = new Uint8Array(data)
5516
if ((dataslice[0] == 123) || (dataslice[0] == 0)) {
5482
- var datapart = new Uint8Array(end - start + 1);
5517
+ var datapart = new Uint8Array(data.byteLength + 1);
5518
datapart.set(dataslice, 1); // Add a zero char at the start of the send, this will indicate that it's not a JSON command.
5519
files.send(datapart);
5520
} else {
@@ -5487,7 +5522,13 @@
5522
}
5523
uploadFile.xptr = end;
5524
Q('d2progressBar').value = end;
5490
- }
5525
+ if (uploadFile.xptr >= uploadFile.xfile.size) {
5526
+ files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr }));
5527
+ } else {
5528
+ if (uploadFile.xdataPriming > 0) { uploadFile.xdataPriming--; p13uploadNextPart(true); }
5529
+ }
5530
+ };
5531
+ uploadFile.xreader.readAsArrayBuffer(uploadFile.xfile.slice(uploadFile.xptr, end));
5532
}
5533
5534
//