Skip to content
Permalink
Browse files
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>
  • Loading branch information
jacobmllr95 and Hiws committed Aug 25, 2020
1 parent 06b6063 commit d54b240adeb6eadfe8736f4926384a5c4d351bde
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 61 deletions.
@@ -151,6 +151,7 @@
"nuxt": "^2.14.3",
"postcss-cli": "^7.1.1",
"prettier": "1.14.3",
"regenerator-runtime": "^0.13.7",
"require-context": "^1.1.0",
"rollup": "^2.26.5",
"rollup-plugin-babel": "^4.4.0",
@@ -1,4 +1,5 @@
// Creates a web-types.json, tags.json and attributes.json files and places them in /dist
require('regenerator-runtime/runtime')
const path = require('path')
const fs = require('fs')
const requireContext = require('require-context')
@@ -24,6 +24,51 @@ const VALUE_EMPTY_DEPRECATED_MSG =

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

// Drop handler function to get all files
/* istanbul ignore next: not supported in JSDOM */
const getAllFileEntries = async dataTransferItemList => {
const fileEntries = []
const queue = []
// Unfortunately `dataTransferItemList` is not iterable i.e. no `.forEach()`
for (let i = 0; i < dataTransferItemList.length; i++) {
queue.push(dataTransferItemList[i].webkitGetAsEntry())
}
while (queue.length > 0) {
const entry = queue.shift()
if (entry.isFile) {
fileEntries.push(entry)
} else if (entry.isDirectory) {
queue.push(...(await readAllDirectoryEntries(entry.createReader())))
}
}
return fileEntries
}

// Get all the entries (files or sub-directories) in a directory
// by calling `.readEntries()` until it returns empty array
/* istanbul ignore next: not supported in JSDOM */
const readAllDirectoryEntries = async directoryReader => {
const entries = []
let readEntries = await readEntriesPromise(directoryReader)
while (readEntries.length > 0) {
entries.push(...readEntries)
readEntries = await readEntriesPromise(directoryReader)
}
return entries
}

// Wrap `.readEntries()` in a promise to make working with it easier
// `.readEntries()` will return only some of the entries in a directory
// (e.g. Chrome returns at most 100 entries at a time)
/* istanbul ignore next: not supported in JSDOM */
const readEntriesPromise = async directoryReader => {
try {
return await new Promise((resolve, reject) => {
directoryReader.readEntries(resolve, reject)
})
} catch {}
}

// @vue/component
export const BFormFile = /*#__PURE__*/ Vue.extend({
name: NAME,
@@ -202,40 +247,23 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
// Always emit original event
this.$emit('change', evt)
// Check if special `items` prop is available on event (drop mode)
// Can be disabled by setting no-traverse
const items = evt.dataTransfer && evt.dataTransfer.items
// Can be disabled by setting `no-traverse`
const { files, items } = evt.dataTransfer || {}
/* istanbul ignore next: not supported in JSDOM */
if (items && !this.noTraverse) {
const queue = []
for (let i = 0; i < items.length; i++) {
const item = items[i].webkitGetAsEntry()
if (item) {
queue.push(this.traverseFileTree(item))
}
}
Promise.all(queue).then(filesArr => {
this.setFiles(arrayFrom(filesArr))
getAllFileEntries(items).then(files => {
this.setFiles(files)
})
return
} else {
// Normal handling
this.setFiles(evt.target.files || files)
}
// Normal handling
this.setFiles(evt.target.files || evt.dataTransfer.files)
},
setFiles(files = []) {
if (!files) {
/* istanbul ignore next: this will probably not happen */
this.selectedFile = null
} else if (this.multiple) {
// Convert files to array
const filesArray = []
for (let i = 0; i < files.length; i++) {
filesArray.push(files[i])
}
// Return file(s) as array
this.selectedFile = filesArray
setFiles(files) {
if (this.multiple) {
this.selectedFile = arrayFrom(files || [])
} else {
// Return single file object
this.selectedFile = files[0] || null
this.selectedFile = files ? files[0] || null : null
}
},
onReset() {
@@ -265,39 +293,12 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
return
}
this.dragging = false
if (evt.dataTransfer.files && evt.dataTransfer.files.length > 0) {
this.onFileChange(evt)
}
},
/* istanbul ignore next: not supported in JSDOM */
traverseFileTree(item, path) /* istanbul ignore next */ {
// Based on https://stackoverflow.com/questions/3590058
return new Promise(resolve => {
path = path || ''
if (item.isFile) {
// Get file
item.file(file => {
file.$path = path // Inject $path to file obj
resolve(file)
})
} else if (item.isDirectory) {
// Get folder contents
item.createReader().readEntries(entries => {
const queue = []
for (let i = 0; i < entries.length; i++) {
queue.push(this.traverseFileTree(entries[i], path + item.name + '/'))
}
Promise.all(queue).then(filesArr => {
resolve(arrayFrom(filesArr))
})
})
}
})
this.onFileChange(evt)
}
},
render(h) {
// Form Input
const input = h('input', {
const $input = h('input', {
ref: 'input',
class: [
{
@@ -317,11 +318,11 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
})

if (this.plain) {
return input
return $input
}

// Overlay Labels
const label = h(
const $label = h(
'label',
{
staticClass: 'custom-file-label',
@@ -352,7 +353,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({
drop: this.onDrop
}
},
[input, label]
[$input, $label]
)
}
})
@@ -11580,7 +11580,7 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==

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

0 comments on commit d54b240

Please sign in to comment.