-
Notifications
You must be signed in to change notification settings - Fork 2k
/
index.js
96 lines (88 loc) · 4.37 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import getFilesAndDirectoriesFromDirectory from './getFilesAndDirectoriesFromDirectory.js'
/**
* Polyfill for the new (experimental) getAsFileSystemHandle API (using the popular webkitGetAsEntry behind the scenes)
* so that we can switch to the getAsFileSystemHandle API once it (hopefully) becomes standard
*/
function getAsFileSystemHandleFromEntry (entry, logDropError) {
if (entry == null) return entry
return {
// eslint-disable-next-line no-nested-ternary
kind: entry.isFile ? 'file' : entry.isDirectory ? 'directory' : undefined,
name: entry.name,
getFile () {
return new Promise((resolve, reject) => entry.file(resolve, reject))
},
async* values () {
// If the file is a directory.
const directoryReader = entry.createReader()
const entries = await new Promise(resolve => {
getFilesAndDirectoriesFromDirectory(directoryReader, [], logDropError, {
onSuccess: (dirEntries) => resolve(dirEntries.map(file => getAsFileSystemHandleFromEntry(file, logDropError))),
})
})
yield* entries
},
}
}
async function* createPromiseToAddFileOrParseDirectory (entry, relativePath, lastResortFile = undefined) {
const relativePathWithName = relativePath ? `${relativePath}/${entry.name}` : null
// For each dropped item, - make sure it's a file/directory, and start deepening in!
if (entry.kind === 'file') {
const file = await entry.getFile()
if (file != null) {
file.relativePath = relativePathWithName
yield file
} else if (lastResortFile != null) yield lastResortFile
} else if (entry.kind === 'directory') {
for await (const handle of entry.values()) {
// Recurse on the directory, appending the dir name to the relative path
yield* createPromiseToAddFileOrParseDirectory(handle, relativePathWithName ?? entry.name)
}
} else if (lastResortFile != null) yield lastResortFile
}
/**
* Load all files from data transfer, and recursively read any directories.
* Note that IE is not supported for drag-drop, because IE doesn't support Data Transfers
*
* @param {DataTransfer} dataTransfer
* @param {*} logDropError on error
*/
export default async function* getFilesFromDataTransfer (dataTransfer, logDropError) {
// Retrieving the dropped items must happen synchronously
// otherwise only the first item gets treated and the other ones are garbage collected.
// https://github.com/transloadit/uppy/pull/3998
const fileSystemHandles = await Promise.all(Array.from(dataTransfer.items, async item => {
let fileSystemHandle
// TODO enable getAsFileSystemHandle API once we can get it working with subdirectories
// IMPORTANT: Need to check isSecureContext *before* calling getAsFileSystemHandle
// or else Chrome will crash when running in HTTP: https://github.com/transloadit/uppy/issues/4133
// if (window.isSecureContext && item.getAsFileSystemHandle != null) entry = await item.getAsFileSystemHandle()
// `webkitGetAsEntry` exists in all popular browsers (including non-WebKit browsers),
// however it may be renamed to getAsEntry() in the future, so you should code defensively, looking for both.
// from https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
const getAsEntry = () => (typeof item.getAsEntry === 'function' ? item.getAsEntry() : item.webkitGetAsEntry())
// eslint-disable-next-line prefer-const
fileSystemHandle ??= getAsFileSystemHandleFromEntry(getAsEntry(), logDropError)
return {
fileSystemHandle,
lastResortFile: item.getAsFile(), // can be used as a fallback in case other methods fail
}
}))
for (const { lastResortFile, fileSystemHandle } of fileSystemHandles) {
// fileSystemHandle and lastResortFile can be null when we drop an url.
if (fileSystemHandle != null) {
try {
yield* createPromiseToAddFileOrParseDirectory(fileSystemHandle, '', lastResortFile)
} catch (err) {
// Example: If dropping a symbolic link, Chromium will throw:
// "DOMException: A requested file or directory could not be found at the time an operation was processed.",
// So we will use lastResortFile instead. See https://github.com/transloadit/uppy/issues/3505.
if (lastResortFile != null) {
yield lastResortFile
} else {
logDropError(err)
}
}
} else if (lastResortFile != null) yield lastResortFile
}
}