这是一篇很长的文章,但我厌倦了所有这些例子都不能正常工作,因为它们使用Promise对象或者一个errant
this
,当您使用Reactjs时,它的含义就不同了。我的实现使用了DropZone和reactjs,当其他方法都无法使用时,我使用了类似于下面这个网站发布的框架来获取字节:
https://www.mokuji.me/article/drop-upload-tutorial-1。对我而言,有两个关键点:
- 您必须使用FileReader的onload函数从事件对象中获取字节。
我尝试了各种组合,但最后起作用的是:
const bytes = e.target.result.split('base64,')[1];
其中,
e
是事件。React需要
const
,您可以在普通JavaScript中使用
var
。但是这给了我Base64编码的字节字符串。
因此,我将只包括适用于集成的适用行,就像您正在使用React一样,因为那是我构建它的方式,但是要尝试将其概括,并在必要时添加注释,使其适用于vanilla JavaScript实现-附带注意我没有在这样的结构中使用它来测试它。
这些将是您在React框架(与vanilla JavaScript实现无关)中的顶部绑定:
this.uploadFile = this.uploadFile.bind(this)
this.processFile = this.processFile.bind(this)
this.errorHandler = this.errorHandler.bind(this)
this.progressHandler = this.progressHandler.bind(this)
如果您在使用React,您需要在DropZone元素中添加onDrop={this.uploadFile}
。如果您没有使用React,这相当于添加当您点击“上传文件”按钮时要运行的onclick事件处理器。
<button onclick="uploadFile(event);" value="Upload File" />
接下来是函数(适用的行...我会省略重置上传进度指示器等操作):
uploadFile(event){
this.setState({
files: event,
});
console.log('File count: ' + this.state.files.length);
const in_files = this.state.files;
if (in_files.length > 0) {
for (let i = 0; i < in_files.length; i++) {
const a = i + 1;
console.log('in loop, pass: ' + a);
const f = in_files[i];
const reader = new FileReader();
reader.onerror = this.errorHandler;
reader.onprogress = this.progressHandler;
reader.onload = this.processFile(f);
reader.readAsDataURL(f);
}
}
}
有一个问题是关于 vanilla JS 的语法,如何获取文件对象:
JavaScript/HTML5/jQuery Drag-And-Drop Upload - "Uncaught TypeError: Cannot read property 'files' of undefined"
请注意,如果您在构造函数中添加files: []
到this.state = { .... }
,React 的 DropZone 已经将文件对象放入this.state.files
中。我添加了那篇文章上的答案中的语法,告诉你如何获取文件对象。它应该可以工作,或者还有其他的帖子可以帮助。但是所有的 Q/A 只是告诉我如何获取File
对象,而不是 blob 数据本身。即使我像 sebu 的回答中一样添加了fileData = new Blob([files[0]]);
,它没有告诉我如何读取这个 blob 的内容,并且如何在没有 Promise 对象的情况下执行它。所以这就是 FileReader 的作用,尽管我实际上尝试并发现我无法使用他们的readAsArrayBuffer
。
您将需要拥有与此结构相关的其他函数——一个处理onerror
,一个处理onprogress
(两者都显示在下面),然后是主要的函数onload
,一旦在最后一行调用reader
上的方法,它就会实际执行工作。从我可以看到的内容来看,基本上是将您的event.dataTransfer.files[0]
直接传递到该onload
函数中。
所以onload
方法调用了我的processFile()
函数(只适用于相关行):
processFile(theFile) {
return function(e) {
const bytes = e.target.result.split('base64,')[1];
}
}
需要将 bytes
转换为 base64 字节。
附加功能:
errorHandler(e){
switch (e.target.error.code) {
case e.target.error.NOT_FOUND_ERR:
alert('File not found.');
break;
case e.target.error.NOT_READABLE_ERR:
alert('File is not readable.');
break;
case e.target.error.ABORT_ERR:
break;
default:
alert('An error occurred reading this file.');
break;
}
}
progressHandler(e) {
if (e.lengthComputable){
const loaded = Math.round((e.loaded / e.total) * 100);
let zeros = '';
if (loaded >= 0 && loaded < 10) {
zeros = '00';
}
else if (loaded < 100) {
zeros = '0';
}
document.getElementById("progress").textContent = zeros + loaded.toString();
document.getElementById("progressBar").style.width = loaded + '%';
}
}
适用的进度指示器标记:
<table id="tblProgress">
<tbody>
<tr>
<td><b><span id="progress">000</span>%</b> <span className="progressBar"><span id="progressBar" /></span></td>
</tr>
</tbody>
</table>
还有CSS:
.progressBar {
background-color: rgba(255, 255, 255, .1);
width: 100%;
height: 26px;
}
#progressBar {
background-color: rgba(87, 184, 208, .5);
content: '';
width: 0;
height: 26px;
}
结语:
在 processFile()
函数中,由于某种原因,我无法将 bytes
添加到我在 this.state
中设置的变量中。 因此,我直接将其设置为我的 JSON 对象 RequestForm
中的变量 attachments
,这个变量也是我使用的 this.state
相同的对象。 attachments
是一个数组,所以我可以推送多个文件。 这就像这样:
const fileArray = [];
if (RequestForm.state.attachments.length > 0) {
for (let i=0; i < RequestForm.state.attachments.length; i++) {
fileArray.push(RequestForm.state.attachments[i]);
}
}
fileArray.push(bytes);
RequestForm.setState({
attachments: fileArray,
});
因为this.state
已经包含了RequestForm
,所以:
this.stores = [
RequestForm,
]
从那时起,我可以将其称为this.state.attachments
。这是React中的一个特性,在普通的JS中不适用。你可以在纯JavaScript中使用全局变量构建类似的结构,并相应地推入(push),但是这样做要容易得多:
var fileArray = new Array();
var newFileArray = [];
if (fileArray.length > 0) {
for (var i=0; i < fileArray.length; i++) {
newFileArray.push(fileArray[i]);
}
}
newFileArray.push(bytes);
fileArray = newFileArray;
然后您只需引用fileArray
,枚举其中的任何文件字节字符串,例如var myBytes = fileArray[0];
表示第一个文件。