加载文件项后如何仅从 dataTransfer 调度 FileList
Posted
技术标签:
【中文标题】加载文件项后如何仅从 dataTransfer 调度 FileList【英文标题】:How to only dispatch FileList from dataTransfer after File items are loaded 【发布时间】:2017-03-08 06:27:58 【问题描述】:我正在使用名为 react-dropzone 和 React-Redux 的 Node 包模块来允许用户将文件拖到窗口上并显示文件信息。
react-dropzone 组件为我提供了标准 event.dataTransfer.files 中的 FileList。
我遇到的问题是,当我将该 FileList 分派到 Redux 存储时,File 项尚未加载到 FileList 数组中,因此当我分派 File 数据时,我只会得到 "preview":"blob:file:///f30eafb8-8a77-4402-9111-379590b4a03f"
。
如果我将文件项记录到控制台,它们会显示得很好,因为它们的数据在我查看时已加载,但我不知道如何仅在文件信息加载后发送文件信息。
这是一个示例,说明如果我将其中一个文件项从 FileList 数组记录到控制台。
lastModified: 1473709614000,
lastModifiedDate: Mon Sep 12 2016 12:46:54 GMT-0700 (PDT),
name: "test_image.jpg",
path: "/Users/mitchconquer/Desktop/test_image.jpg",
preview: "blob:file:///0fe69686-125d-464a-a0a8-a42e497d3808",
size: 41805,
type: "image/jpeg",
webkitRelativePath: ""
我认为问题在于 File 对象没有完全加载,但我想不出一种方法来仅在数据完全加载后触发操作。有人能指出我正确的道路吗?
这是我使用 react-dropzone 的组件:
// FileDrop.js
import React, Component, PropTypes from 'react';
import Link from 'react-router';
import styles from './Home.css';
import Dropzone from 'react-dropzone';
export default class FileDrop extends Component
static propTypes =
message: PropTypes.string.isRequired,
setFile: PropTypes.func.isRequired
;
onDrop(files, event)
console.log(files);
this.props.setFile(files);
render()
return (
<div>
<Dropzone onDropAccepted=this.onDrop.bind(this) multiple=false disablePreview=false>
<div>this.props.message</div>
</Dropzone>
</div>
);
...这是我的操作文件,其中包含上述onDrop
方法中使用的setFile
方法:
// actions/files.js
export const SET_FILE = 'SET_FILE';
// ACTION CREATORS
function _setFile(file)
return
type: SET_FILE,
file
;
// ACTION CREATOR CREATORS
export function setFile(file)
return _setFile(file[0]);
...这是处理该操作的减速器:
// reducers/files.js
import SET_FILE from '../actions/files';
export const initialState = [];
export default function files(state = initialState, action = )
switch (action.type)
case SET_FILE:
const newState = state.slice();
const file = action.file;
newState.push(file);
return newState;
default:
return state;
【问题讨论】:
【参考方案1】:对于那些遇到此问题的人,对我而言,我认为问题在于文件列表中的文件不像我认为的那样是“普通的 javascript 对象”,而是在fact 文件/Blob。
我不确定细节——如果有人能解释一下我会很高兴——但是我认为我知道是 blob 没有像标准 JS 那样的键对象这样做,所以你不能只是将它推送到 Redux 存储并查看我期望的属性的键值对 (lastModified, lastModifiedDate, name, path, preview, size, type, webkitRelativePath
)。
如果您只查看 blob,它们唯一的属性是 preview
,因为这是 React Dropzone 在文件/Blob 上专门设置的属性。要查看其他属性,您必须调用 blob 上的每个属性。
这是我下面的解决方案的简化版本。我接受 Filelist 数组,创建一个新对象来设置键值并遍历每个键,并在 newFile
对象上使用其属性设置它。最后我返回那个对象。然后,您可以在您的商店中设置该对象,它将拥有处理文件所需的数据。
onDrop(files)
const fileBlob = files[0];
const newFile = ;
_fileProperties.forEach(key =>
newFile[key] = fileBlob[key];
);
return newFile;
const _fileProperties = [
'lastModified',
'lastModifiedDate',
'name',
'path',
'preview',
'size',
'type',
'webkitRelativePath'
];
可能有很多更好的方法可以做到这一点,我相信我的理解是错误的,所以如果有人能更好地解释这一点,我仍然会喜欢它。与此同时,这个解决方案对我有用。
还有一些其他地方可以获取更多信息,例如MDN's File drag and drop 文章。
【讨论】:
如果你使用 lodash,你也可以直接使用_.toPlainObject(files[0])
。【参考方案2】:
我有同样的问题。我尝试了@Mitch Conquer 提供的解决方案和@Ishmael Smyrnow 的评论,但它返回的是普通对象而不是文件对象。同样正如here 所讨论的,不建议将不可序列化的对象存储在存储中。因此,我所做的是,首先将文件转换为 base64 并仅使用 FileReader 进行存储。
const reader = new FileReader();
reader.readAsDataURL(file);
然后在转换回文件后作为here提供的解决方案。
export const dataURLtoFile = (dataurl, filename) =>
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--)
u8arr[n] = bstr.charCodeAt(n);
return new File([u8arr], filename, type:mime);
【讨论】:
【参考方案3】:我调整了onDrop
属性如下:onDrop=this.onDrop
,并将文件属性添加到我的方法中 - 这对我有用。
我认为上述方法不太奏效的原因是我的组件中有其他输入元素 - 文件上传只是我正在捕获的集合的一部分。
希望这会有所帮助!
【讨论】:
以上是关于加载文件项后如何仅从 dataTransfer 调度 FileList的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Javascript 将文件添加到已经存在的 dataTransfer 对象中
使用 axios.get 方法加载页面时,如何仅从 API 获取数据一次?