🌐 AI搜索 & 代理 主页
Skip to content

Commit d54b240

Browse files
jacobmllr95Hiws
andauthored
fix(b-form-file): drop handling for huge amounts of files (closes #5615) (#5685)
* fix(b-form-file): drop handling for huge amout of files * Update form-file.js * Use `regenerator-runtime` in `create-web-types` script to handle async/await * Update form-file.js * Update form-file.js Co-authored-by: Hiws <rni@nova-c.dk>
1 parent 06b6063 commit d54b240

File tree

4 files changed

+64
-61
lines changed

4 files changed

+64
-61
lines changed

��package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
"nuxt": "^2.14.3",
152152
"postcss-cli": "^7.1.1",
153153
"prettier": "1.14.3",
154+
"regenerator-runtime": "^0.13.7",
154155
"require-context": "^1.1.0",
155156
"rollup": "^2.26.5",
156157
"rollup-plugin-babel": "^4.4.0",

scripts/create-web-types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Creates a web-types.json, tags.json and attributes.json files and places them in /dist
2+
require('regenerator-runtime/runtime')
23
const path = require('path')
34
const fs = require('fs')
45
const requireContext = require('require-context')

src/components/form-file/form-file.js

Lines changed: 61 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,51 @@ const VALUE_EMPTY_DEPRECATED_MSG =
2424

2525
const isValidValue = value => isFile(value) || (isArray(value) && value.every(v => isValidValue(v)))
2626

27+
// Drop handler function to get all files
28+
/* istanbul ignore next: not supported in JSDOM */
29+
const getAllFileEntries = async dataTransferItemList => {
30+
const fileEntries = []
31+
const queue = []
32+
// Unfortunately `dataTransferItemList` is not iterable i.e. no `.forEach()`
33+
for (let i = 0; i < dataTransferItemList.length; i++) {
34+
queue.push(dataTransferItemList[i].webkitGetAsEntry())
35+
}
36+
while (queue.length > 0) {
37+
const entry = queue.shift()
38+
if (entry.isFile) {
39+
fileEntries.push(entry)
40+
} else if (entry.isDirectory) {
41+
queue.push(...(await readAllDirectoryEntries(entry.createReader())))
42+
}
43+
}
44+
return fileEntries
45+
}
46+
47+
// Get all the entries (files or sub-directories) in a directory
48+
// by calling `.readEntries()` until it returns empty array
49+
/* istanbul ignore next: not supported in JSDOM */
50+
const readAllDirectoryEntries = async directoryReader => {
51+
const entries = []
52+
let readEntries = await readEntriesPromise(directoryReader)
53+
while (readEntries.length > 0) {
54+
entries.push(...readEntries)
55+
readEntries = await readEntriesPromise(directoryReader)
56+
}
57+
return entries
58+
}
59+
60+
// Wrap `.readEntries()` in a promise to make working with it easier
61+
// `.readEntries()` will return only some of the entries in a directory
62+
// (e.g. Chrome returns at most 100 entries at a time)
63+
/* istanbul ignore next: not supported in JSDOM */
64+
const readEntriesPromise = async directoryReader => {
65+
try {
66+
return await new Promise((resolve, reject) => {
67+
directoryReader.readEntries(resolve, reject)
68+
})
69+
} catch {}
70+
}
71+
2772
// @vue/component
2873
export const BFormFile = /*#__PURE__*/ Vue.extend({
2974
name: NAME,
@@ -202,40 +247,23 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
202247
// Always emit original event
203248
this.$emit('change', evt)
204249
// Check if special `items` prop is available on event (drop mode)
205-
// Can be disabled by setting no-traverse
206-
const items = evt.dataTransfer && evt.dataTransfer.items
250+
// Can be disabled by setting `no-traverse`
251+
const { files, items } = evt.dataTransfer || {}
207252
/* istanbul ignore next: not supported in JSDOM */
208253
if (items && !this.noTraverse) {
209-
const queue = []
210-
for (let i = 0; i < items.length; i++) {
211-
const item = items[i].webkitGetAsEntry()
212-
if (item) {
213-
queue.push(this.traverseFileTree(item))
214-
}
215-
}
216-
Promise.all(queue).then(filesArr => {
217-
this.setFiles(arrayFrom(filesArr))
254+
getAllFileEntries(items).then(files => {
255+
this.setFiles(files)
218256
})
219-
return
257+
} else {
258+
// Normal handling
259+
this.setFiles(evt.target.files || files)
220260
}
221-
// Normal handling
222-
this.setFiles(evt.target.files || evt.dataTransfer.files)
223261
},
224-
setFiles(files = []) {
225-
if (!files) {
226-
/* istanbul ignore next: this will probably not happen */
227-
this.selectedFile = null
228-
} else if (this.multiple) {
229-
// Convert files to array
230-
const filesArray = []
231-
for (let i = 0; i < files.length; i++) {
232-
filesArray.push(files[i])
233-
}
234-
// Return file(s) as array
235-
this.selectedFile = filesArray
262+
setFiles(files) {
263+
if (this.multiple) {
264+
this.selectedFile = arrayFrom(files || [])
236265
} else {
237-
// Return single file object
238-
this.selectedFile = files[0] || null
266+
this.selectedFile = files ? files[0] || null : null
239267
}
240268
},
241269
onReset() {
@@ -265,39 +293,12 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
265293
return
266294
}
267295
this.dragging = false
268-
if (evt.dataTransfer.files && evt.dataTransfer.files.length > 0) {
269-
this.onFileChange(evt)
270-
}
271-
},
272-
/* istanbul ignore next: not supported in JSDOM */
273-
traverseFileTree(item, path) /* istanbul ignore next */ {
274-
// Based on https://stackoverflow.com/questions/3590058
275-
return new Promise(resolve => {
276-
path = path || ''
277-
if (item.isFile) {
278-
// Get file
279-
item.file(file => {
280-
file.$path = path // Inject $path to file obj
281-
resolve(file)
282-
})
283-
} else if (item.isDirectory) {
284-
// Get folder contents
285-
item.createReader().readEntries(entries => {
286-
const queue = []
287-
for (let i = 0; i < entries.length; i++) {
288-
queue.push(this.traverseFileTree(entries[i], path + item.name + '/'))
289-
}
290-
Promise.all(queue).then(filesArr => {
291-
resolve(arrayFrom(filesArr))
292-
})
293-
})
294-
}
295-
})
296+
this.onFileChange(evt)
296297
}
297298
},
298299
render(h) {
299300
// Form Input
300-
const input = h('input', {
301+
const $input = h('input', {
301302
ref: 'input',
302303
class: [
303304
{
@@ -317,11 +318,11 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
317318
})
318319

319320
if (this.plain) {
320-
return input
321+
return $input
321322
}
322323

323324
// Overlay Labels
324-
const label = h(
325+
const $label = h(
325326
'label',
326327
{
327328
staticClass: 'custom-file-label',
@@ -352,7 +353,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
352353
drop: this.onDrop
353354
}
354355
},
355-
[input, label]
356+
[$input, $label]
356357
)
357358
}
358359
})

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11580,7 +11580,7 @@ regenerator-runtime@^0.11.0:
1158011580
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
1158111581
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
1158211582

11583-
regenerator-runtime@^0.13.4:
11583+
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
1158411584
version "0.13.7"
1158511585
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
1158611586
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==

0 commit comments

Comments
 (0)