Skip to content

Commit 8d998e0

Browse files
committed
feat(tpl/upload): use File System Handle API if possible
1 parent 676446f commit 8d998e0

File tree

1 file changed

+159
-99
lines changed

1 file changed

+159
-99
lines changed

src/tpl/frontend/index.js

Lines changed: 159 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
(function () {
22
var strUndef = 'undefined';
3+
var protoHttps = 'https:';
34

45
var classNone = 'none';
56
var classHeader = 'header';
@@ -550,94 +551,159 @@
550551
return ts;
551552
}
552553

553-
function entriesToFiles(entries, onDone) {
554-
var files = [];
555-
var len = entries.length;
556-
var cb = 0;
557-
if (!len) {
558-
onDone(files);
559-
}
560-
561-
function increaseCb() {
562-
cb++;
563-
if (cb === len) {
564-
onDone(files);
554+
var itemsToFiles;
555+
if (location.protocol === protoHttps && typeof FileSystemHandle !== strUndef && !DataTransferItem.prototype.webkitGetAsEntry) {
556+
var handleKindFile = 'file';
557+
var handleKindDir = 'directory';
558+
var permDescriptor = {mode: 'read'};
559+
itemsToFiles = function (dataTransferItems, canMkdir, onDone, onLacksMkdir) {
560+
function resultsToFiles(results, files, dirPath) {
561+
return Promise.all(results.map(function (result) {
562+
var handle = result.value;
563+
if (handle.kind === handleKindFile) {
564+
return handle.queryPermission(permDescriptor).then(function (queryResult) {
565+
if (queryResult === 'prompt') return handle.requestPermission(permDescriptor);
566+
}).then(function () {
567+
return handle.getFile();
568+
}).then(function (file) {
569+
var relativePath = dirPath + file.name;
570+
files.push({file: file, relativePath: relativePath});
571+
})['catch'](function (err) { // workaround IE8- syntax error for ".catch"(reserved keyword)
572+
typeof console !== strUndef && console.error(err);
573+
});
574+
} else if (handle.kind === handleKindDir) {
575+
return new Promise(function (resolve) {
576+
var childResults = [];
577+
var childIter = handle.values();
578+
579+
function onLevelDone() {
580+
childResults = null;
581+
childIter = null;
582+
resolve();
583+
}
584+
585+
function addChildResult() {
586+
childIter.next().then(function (result) {
587+
if (result.done) {
588+
if (childResults.length) {
589+
resultsToFiles(childResults, files, dirPath + handle.name + '/').then(onLevelDone);
590+
} else onLevelDone();
591+
} else {
592+
childResults.push(result);
593+
addChildResult();
594+
}
595+
});
596+
}
597+
598+
addChildResult();
599+
});
600+
}
601+
}));
565602
}
566-
}
567603

568-
entries.forEach(function (entry) {
569-
if (entry.isFile) {
570-
var relativePath = entry.fullPath;
571-
if (relativePath[0] === '/') {
572-
relativePath = relativePath.substring(1);
604+
var files = [];
605+
var hasDir = false;
606+
if (!dataTransferItems || !dataTransferItems.length) return onDone(files, hasDir);
607+
608+
var items = Array.prototype.slice.call(dataTransferItems);
609+
Promise.all(items.map(function (item) {
610+
return item.getAsFileSystemHandle();
611+
})).then(function (handles) {
612+
handles = handles.filter(Boolean); // undefined for pasted content
613+
hasDir = handles.some(function (handle) {
614+
return handle.kind === handleKindDir;
615+
});
616+
if (hasDir && !canMkdir) {
617+
return onLacksMkdir();
573618
}
574-
entry.file(function (file) {
575-
files.push({file: file, relativePath: relativePath});
576-
increaseCb();
577-
}, function (err) {
578-
increaseCb();
579-
typeof console !== strUndef && console.error(err);
619+
var handleResults = handles.map(function (handle) {
620+
return {value: handle, done: false};
580621
});
581-
} else if (entry.isDirectory) {
582-
var reader = entry.createReader();
583-
reader.readEntries(function (subEntries) {
584-
if (subEntries.length) {
585-
entriesToFiles(subEntries, function (subFiles) {
586-
Array.prototype.push.apply(files, subFiles);
622+
resultsToFiles(handleResults, files, '').then(function () {
623+
onDone(files, hasDir);
624+
});
625+
});
626+
}
627+
} else {
628+
itemsToFiles = function (dataTransferItems, canMkdir, onDone, onLacksMkdir) {
629+
function entriesToFiles(entries, files, onLevelDone) {
630+
var len = entries.length;
631+
var cb = 0;
632+
if (!len) return onLevelDone();
633+
634+
function increaseCb() {
635+
cb++;
636+
if (cb === len) {
637+
onLevelDone();
638+
}
639+
}
640+
641+
entries.forEach(function (entry) {
642+
if (entry.isFile) {
643+
var relativePath = entry.fullPath;
644+
if (relativePath[0] === '/') {
645+
relativePath = relativePath.substring(1);
646+
}
647+
entry.file(function (file) {
648+
files.push({file: file, relativePath: relativePath});
649+
increaseCb();
650+
}, function (err) {
587651
increaseCb();
652+
typeof console !== strUndef && console.error(err);
653+
});
654+
} else if (entry.isDirectory) {
655+
var reader = entry.createReader();
656+
reader.readEntries(function (subEntries) {
657+
if (subEntries.length) {
658+
entriesToFiles(subEntries, files, increaseCb);
659+
} else {
660+
increaseCb();
661+
}
588662
});
589-
} else {
590-
increaseCb();
591663
}
592664
});
593665
}
594-
});
595-
}
596666

597-
function itemsToFiles(dataTransferItems, onDone) {
598-
var files = [];
599-
var len = dataTransferItems.length;
600-
if (!len) {
601-
onDone(files);
602-
}
667+
var files = [];
668+
var hasDir = false;
669+
if (!dataTransferItems || !dataTransferItems.length || !dataTransferItems[0].webkitGetAsEntry) return onDone(files, hasDir);
603670

604-
var entries = [];
605-
for (var i = 0; i < len; i++) {
606-
var item = dataTransferItems[i];
607-
var entry = item.webkitGetAsEntry();
608-
if (!entry) {
609-
continue;
610-
}
611-
if (entry.isFile) {
612-
// Safari cannot get file from entry by entry.file(), if it is a pasted image
613-
// so workaround is for all browsers, just get first hierarchy of files by item.getAsFile()
614-
var file = item.getAsFile();
615-
files.push({file: file, relativePath: file.name});
616-
} else if (entry.isDirectory) {
617-
entries.push(entry);
671+
var entries = [];
672+
for (var i = 0, len = dataTransferItems.length; i < len; i++) {
673+
var item = dataTransferItems[i];
674+
var entry = item.webkitGetAsEntry();
675+
if (!entry) { // undefined for pasted text
676+
continue;
677+
}
678+
if (entry.isFile) {
679+
// Safari cannot get file from entry by entry.file(), if it is a pasted image
680+
// so workaround is for all browsers, just get first hierarchy of files by item.getAsFile()
681+
var file = item.getAsFile();
682+
files.push({file: file, relativePath: file.name});
683+
} else if (entry.isDirectory) {
684+
hasDir = true;
685+
if (canMkdir) {
686+
entries.push(entry);
687+
} else {
688+
return onLacksMkdir();
689+
}
690+
}
618691
}
619-
}
620692

621-
entriesToFiles(entries, function (entryFiles) {
622-
files.push.apply(files, entryFiles);
623-
onDone(files);
624-
});
693+
entriesToFiles(entries, files, function () {
694+
onDone(files, hasDir);
695+
});
696+
}
625697
}
626698

627-
function itemsHasDir(dataTransferItems) {
628-
if (!dataTransferItems) {
629-
return false;
630-
}
631-
var items = Array.prototype.slice.call(dataTransferItems);
632-
if (items.length && items[0].webkitGetAsEntry) {
633-
for (var i = 0, len = items.length; i < len; i++) {
634-
var entry = items[i].webkitGetAsEntry();
635-
if (entry && entry.isDirectory) {
636-
return true;
637-
}
699+
function dataTransferToFiles(dataTransfer, canMkdir, onDone, onLacksMkdir) {
700+
itemsToFiles(dataTransfer.items, canMkdir, function (files, hasDir) {
701+
// ancient Browser
702+
if (files.length === 0 && dataTransfer.files && dataTransfer.files.length) {
703+
files = Array.prototype.slice.call(dataTransfer.files);
638704
}
639-
}
640-
return false;
705+
onDone(files, hasDir);
706+
}, onLacksMkdir);
641707
}
642708

643709
function switchToFileMode() {
@@ -934,28 +1000,22 @@
9341000
return;
9351001
}
9361002

937-
var items = e.dataTransfer.items;
938-
if (itemsHasDir(items)) {
939-
// must use progressive upload by JS if has directory
940-
if (!canMkdir || !uploadProgressively) {
941-
typeof showUploadDirFailMessage !== strUndef && showUploadDirFailMessage();
942-
return;
943-
}
944-
itemsToFiles(items, function (files) {
1003+
dataTransferToFiles(e.dataTransfer, canMkdir && Boolean(uploadProgressively), function (files, hasDir) {
1004+
if (hasDir) {
9451005
switchToDirMode();
9461006
uploadProgressively(files);
947-
});
948-
} else {
949-
switchToFileMode();
950-
951-
if (uploadProgressively) {
952-
var files = Array.prototype.slice.call(e.dataTransfer.files);
953-
uploadProgressively(files);
9541007
} else {
955-
fileInput.files = e.dataTransfer.files;
956-
form.submit();
1008+
switchToFileMode();
1009+
if (uploadProgressively) {
1010+
uploadProgressively(files);
1011+
} else {
1012+
fileInput.files = files;
1013+
form.submit();
1014+
}
9571015
}
958-
}
1016+
}, function () {
1017+
typeof showUploadDirFailMessage !== strUndef && showUploadDirFailMessage();
1018+
});
9591019
}
9601020

9611021
document.body.addEventListener('dragstart', onSelfDragStart);
@@ -1066,32 +1126,32 @@
10661126
generatePastedFiles(data);
10671127
return;
10681128
}
1069-
var hasDir = itemsHasDir(items);
1070-
if (hasDir && !canMkdir) {
1071-
typeof showUploadDirFailMessage !== strUndef && showUploadDirFailMessage();
1072-
return;
1073-
}
1074-
itemsToFiles(items, function (files) {
1129+
1130+
itemsToFiles(items, canMkdir, function (files, hasDir) {
1131+
// for pasted text
10751132
if (!files.length) {
1076-
// for pasted text
10771133
generatePastedFiles(data);
10781134
return;
10791135
}
1136+
1137+
// suppose for pasted image data
10801138
if (files.length === 1 && files[0].file.type === 'image/png') {
1081-
// suppose for pasted image
10821139
files = files.map(function (fileInfo) {
10831140
return fileInfo && fileInfo.file;
10841141
});
10851142
generatePastedFiles({files: files});
10861143
return;
10871144
}
1088-
// pasted real files, not image binary
1145+
1146+
// pasted real files
10891147
if (hasDir) {
10901148
switchToDirMode();
10911149
} else {
10921150
switchToFileMode();
10931151
}
10941152
uploadProgressively(files);
1153+
}, function () {
1154+
typeof showUploadDirFailMessage !== strUndef && showUploadDirFailMessage();
10951155
});
10961156
});
10971157
}

0 commit comments

Comments
 (0)