From 5d1edc888e808385c3fccbfe3fd72c6d282a6e92 Mon Sep 17 00:00:00 2001 From: Ching En Cheng Date: Tue, 16 Mar 2021 16:40:28 +0100 Subject: [PATCH 1/5] return proper fullPath when triggered via file selector --- src/Html5FileSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Html5FileSelector.js b/src/Html5FileSelector.js index db5b5b9..6de7b82 100644 --- a/src/Html5FileSelector.js +++ b/src/Html5FileSelector.js @@ -70,7 +70,7 @@ function packageFile(file, entry) { } return { fileObject: file, // provide access to the raw File object (required for uploading) - fullPath: entry ? copyString(entry.fullPath) : file.name, + fullPath: entry ? copyString(entry.fullPath) : file.webkitRelativePath, lastModified: file.lastModified, lastModifiedDate: file.lastModifiedDate, name: file.name, From d5b9cdff4532142ee5291e02f0b0d6b5796d6106 Mon Sep 17 00:00:00 2001 From: Ching En Cheng Date: Wed, 16 Jun 2021 14:22:35 +0200 Subject: [PATCH 2/5] bump version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a0cb53..28a29e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "html5-file-selector", - "version": "2.1.0", + "version": "2.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 53420cd..aa045b7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "html5-file-selector", - "version": "2.1.0", + "version": "2.1.1", "description": "A wrapper library for more easily handling html5 drag/drop and file input file and folder selections", "homepage": "https://www.github.com/quarklemotion/html5-file-selector", "repository": "quarklemotion/html5-file-selector", From 1625c2102e49f8f997e16108f6f0d5b6bfd6b2d8 Mon Sep 17 00:00:00 2001 From: Ching En Cheng Date: Fri, 18 Jun 2021 15:38:31 +0200 Subject: [PATCH 3/5] fix null full path from fileSelectDialog --- src/Html5FileSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Html5FileSelector.js b/src/Html5FileSelector.js index 6de7b82..8ca83b5 100644 --- a/src/Html5FileSelector.js +++ b/src/Html5FileSelector.js @@ -70,7 +70,7 @@ function packageFile(file, entry) { } return { fileObject: file, // provide access to the raw File object (required for uploading) - fullPath: entry ? copyString(entry.fullPath) : file.webkitRelativePath, + fullPath: entry ? copyString(entry.fullPath) : (file.webkitRelativePath !== '' ? file.webkitRelativePath : `/${file.name}`), lastModified: file.lastModified, lastModifiedDate: file.lastModifiedDate, name: file.name, From c6c32c34548e7223598ccb6336e572bf48837dd5 Mon Sep 17 00:00:00 2001 From: ccheng-dev Date: Fri, 13 May 2022 13:40:45 +0200 Subject: [PATCH 4/5] support file handle --- src/Html5FileSelector.js | 131 ++++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 28 deletions(-) diff --git a/src/Html5FileSelector.js b/src/Html5FileSelector.js index 8ca83b5..c6669f3 100644 --- a/src/Html5FileSelector.js +++ b/src/Html5FileSelector.js @@ -1,6 +1,34 @@ const DEFAULT_FILES_TO_IGNORE = [ '.DS_Store', // OSX indexing file - 'Thumbs.db' // Windows indexing file + '.ds_store', + 'Thumbs.db', // Windows indexing file + '.*~', + '~$*', + '.~lock.*', + '~*.tmp', + '*.~*', + '._*', + '.*.sw?', + '.*.*sw?', + '.TemporaryItems', + '.Trashes', + '.DocumentRevisions-V100', + '.Trash-*', + '.fseventd', + '.apdisk', + '.directory', + '*.part', + '*.filepart', + '*.crdownload', + '*.kate-swp', + '*.gnucash.tmp-*', + '.synkron.*', + '.sync.ffs_db', + '.symform', + '.symform-store', + '.fuse_hidden*', + '*.unison', + '.nfs*', ]; // map of common (mostly media types) mime types to use when the browser does not supply the mime type @@ -19,7 +47,8 @@ const EXTENSION_TO_MIME_TYPE_MAP = { }; function shouldIgnoreFile(file) { - return DEFAULT_FILES_TO_IGNORE.indexOf(file.name) >= 0; + const micromatch = require('micromatch'); + return micromatch.isMatch(file.name, DEFAULT_FILES_TO_IGNORE); } function copyString(aString) { @@ -58,6 +87,29 @@ function traverseDirectory(entry) { }); } +function traverseDirectoryHandle(listItem) { + return new Promise(async (resolveDirectory) => { + const directoryHandle = await listItem.getAsFileSystemHandle(); + const iterationAttempts = []; + + async function* getFileHandlesRecursively(handle) { + if (handle.kind === 'file') { + yield handle; + } else if (handle.kind === 'directory') { + for await (const folderHandle of handle.values()) { + yield* getFileHandlesRecursively(folderHandle); + } + } + }; + + for await (const fileHandle of getFileHandlesRecursively(directoryHandle)) { + iterationAttempts.push(packageFileHandle(fileHandle, directoryHandle)); + } + + resolveDirectory(iterationAttempts); + }); +} + // package the file in an object that includes the fullPath from the file entry // that would otherwise be lost function packageFile(file, entry) { @@ -80,11 +132,31 @@ function packageFile(file, entry) { }; } -function getFile(entry) { - return new Promise((resolve) => { - entry.file((file) => { - resolve(packageFile(file, entry)); - }); +function packageFileHandle(fileHandle, folderHandle) { + return new Promise(async (resolve) => { + const file = await fileHandle.getFile(); + let fileData = packageFile(file); + fileData.fileObject = fileHandle; + + if (folderHandle) { + let pathArray = await folderHandle.resolve(fileHandle); + const path = pathArray.join('/'); + fileData.fullPath = `/${folderHandle.name}/${path}`; + } + resolve(fileData); + }); +} + +function getFile(entry, listItem = null) { + return new Promise(async (resolve) => { + if (typeof entry.getFile === 'function' || listItem) { + const fileHandle = listItem ? await listItem.getAsFileSystemHandle() : entry.getFile(); + resolve(packageFileHandle(fileHandle)); + } else { + entry.file((file) => { + resolve(packageFile(file, entry)); + }); + } }); } @@ -99,34 +171,38 @@ function handleFilePromises(promises, fileList) { }); } -export function getDataTransferFiles(dataTransfer) { +export function getDataTransferFiles(dataTransfer, fileHandle) { const dataTransferFiles = []; const folderPromises = []; const filePromises = []; - [].slice.call(dataTransfer.items).forEach((listItem) => { + [].slice.call(dataTransfer.items).forEach(async (listItem) => { if (typeof listItem.webkitGetAsEntry === 'function') { const entry = listItem.webkitGetAsEntry(); - - if (entry) { - if (entry.isDirectory) { - folderPromises.push(traverseDirectory(entry)); - } else { - filePromises.push(getFile(entry)); - } + + if (!entry) return; + if (entry.isDirectory) { + const promise = fileHandle ? traverseDirectoryHandle(listItem) : traverseDirectory(entry); + folderPromises.push(promise); + } else { + const fileHandleItem = fileHandle ? listItem : null; + filePromises.push(getFile(entry, fileHandleItem)); } } else { dataTransferFiles.push(listItem); } }); if (folderPromises.length) { - const flatten = (array) => array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []); - return Promise.all(folderPromises).then((fileEntries) => { - const flattenedEntries = flatten(fileEntries); - // collect async promises to convert each fileEntry into a File object - flattenedEntries.forEach((fileEntry) => { - filePromises.push(getFile(fileEntry)); - }); + return Promise.all(folderPromises).then((promises) => { + if (fileHandle) { + promises[0].forEach((promise) => {filePromises.push(promise);}); + } else { + const flatten = (array) => array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []); + const flattenedEntries = flatten(promises); + // collect async promises to convert each fileEntry into a File object + flattenedEntries.forEach((fileEntry) => {filePromises.push(getFile(fileEntry));}); + } + return handleFilePromises(filePromises, dataTransferFiles); }); } else if (filePromises.length) { @@ -134,7 +210,6 @@ export function getDataTransferFiles(dataTransfer) { } return Promise.resolve(dataTransferFiles); } - /** * This function should be called from both the onDrop event from your drag/drop * dropzone as well as from the HTML5 file selector input field onChange event @@ -144,12 +219,12 @@ export function getDataTransferFiles(dataTransfer) { * Returns: an array of File objects, that includes all files within folders * and subfolders of the dropped/selected items. */ -export function getDroppedOrSelectedFiles(event) { +export function getDroppedOrSelectedFiles(event, fileHandle = true) { const dataTransfer = event.dataTransfer; if (dataTransfer && dataTransfer.items) { - return getDataTransferFiles(dataTransfer).then((fileList) => { - return Promise.resolve(fileList); - }); + return getDataTransferFiles(dataTransfer, fileHandle).then((fileList) => { + return Promise.resolve(fileList); + }); } const files = []; const dragDropFileList = dataTransfer && dataTransfer.files; From 3a03a177f538513bf5694c553e3302e49b38042c Mon Sep 17 00:00:00 2001 From: ccheng-dev Date: Tue, 20 Dec 2022 06:41:41 +0100 Subject: [PATCH 5/5] get fileEntry if browser does not support filehandle --- src/Html5FileSelector.js | 68 ++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/Html5FileSelector.js b/src/Html5FileSelector.js index c6669f3..2bf8a0e 100644 --- a/src/Html5FileSelector.js +++ b/src/Html5FileSelector.js @@ -88,25 +88,28 @@ function traverseDirectory(entry) { } function traverseDirectoryHandle(listItem) { - return new Promise(async (resolveDirectory) => { - const directoryHandle = await listItem.getAsFileSystemHandle(); + return new Promise(async (resolveDirectory, reject) => { const iterationAttempts = []; - - async function* getFileHandlesRecursively(handle) { - if (handle.kind === 'file') { - yield handle; - } else if (handle.kind === 'directory') { - for await (const folderHandle of handle.values()) { - yield* getFileHandlesRecursively(folderHandle); + try { + const directoryHandle = await listItem.getAsFileSystemHandle(); + + async function* getFileHandlesRecursively(handle) { + if (handle.kind === 'file') { + yield handle; + } else if (handle.kind === 'directory') { + for await (const folderHandle of handle.values()) { + yield* getFileHandlesRecursively(folderHandle); + } } + }; + + for await (const fileHandle of getFileHandlesRecursively(directoryHandle)) { + iterationAttempts.push(packageFileHandle(fileHandle, directoryHandle)); } - }; - - for await (const fileHandle of getFileHandlesRecursively(directoryHandle)) { - iterationAttempts.push(packageFileHandle(fileHandle, directoryHandle)); + resolveDirectory(iterationAttempts); + } catch (e) { + reject(e); } - - resolveDirectory(iterationAttempts); }); } @@ -147,15 +150,24 @@ function packageFileHandle(fileHandle, folderHandle) { }); } -function getFile(entry, listItem = null) { - return new Promise(async (resolve) => { - if (typeof entry.getFile === 'function' || listItem) { - const fileHandle = listItem ? await listItem.getAsFileSystemHandle() : entry.getFile(); - resolve(packageFileHandle(fileHandle)); - } else { - entry.file((file) => { - resolve(packageFile(file, entry)); - }); +function getFile(entry) { + return new Promise(async (resolve, reject) => { + try { + if (typeof entry.getAsFileSystemHandle === 'function') { + const fileHandle = await entry.getAsFileSystemHandle(); + resolve(packageFileHandle(fileHandle)); + } + if (typeof entry.getFile === 'function') { + const file = entry.getFile(); + resolve(packageFileHandle(file)); + } + if (typeof entry.file === 'function') { + entry.file((file) => { + resolve(packageFile(file, entry)); + }); + } + } catch (e) { + reject(e); } }); } @@ -179,14 +191,16 @@ export function getDataTransferFiles(dataTransfer, fileHandle) { [].slice.call(dataTransfer.items).forEach(async (listItem) => { if (typeof listItem.webkitGetAsEntry === 'function') { const entry = listItem.webkitGetAsEntry(); - + if (!entry) return; + fileHandle = fileHandle && typeof listItem.getAsFileSystemHandle === 'function'; + if (entry.isDirectory) { const promise = fileHandle ? traverseDirectoryHandle(listItem) : traverseDirectory(entry); folderPromises.push(promise); } else { - const fileHandleItem = fileHandle ? listItem : null; - filePromises.push(getFile(entry, fileHandleItem)); + const fileEntry = fileHandle ? listItem : entry; + filePromises.push(getFile(fileEntry)); } } else { dataTransferFiles.push(listItem);