这是一篇很长的文章,但我厌倦了所有这些对我不起作用的示例,因为它们使用了 Promise 对象或错误的 this,当您使用 Reactjs 时,它们具有不同的含义。我的实现是使用带有 reactjs 的 DropZone,并且我使用类似于以下站点上发布的框架获取字节,而上面的其他任何方法都不起作用:https://www.mokuji.me/article/drop-upload-tutorial-1。对我来说,有 2 把钥匙:
- 您必须在 FileReader 的 onload 函数中使用和期间从事件对象中获取字节。
-
我尝试了各种组合,但最终奏效的是:
const bytes = e.target.result.split('base64,')[1];
e 是事件。 React 需要 const,你可以在纯 Javascript 中使用 var。但这给了我base64编码的字节字符串。
因此,我将包含用于集成它的适用行,就像您使用 React 一样,因为这就是我构建它的方式,但也尝试概括这一点,并在必要时添加 cmets,使其适用于一个普通的 Javascript 实现 - 需要注意的是,我没有在这样的构造中使用它来测试它。
这些将是您在顶部的绑定,在您的构造函数中,在 React 框架中(与普通 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);
您的 DropZone 元素中会包含 onDrop={this.uploadFile}。如果您在没有 React 的情况下执行此操作,这相当于添加您想要在单击“上传文件”按钮时运行的 onclick 事件处理程序。
<button onclick="uploadFile(event);" value="Upload File" />
然后是函数(适用行...我将省略我的重置上传进度指示器等):
uploadFile(event){
// This is for React, only
this.setState({
files: event,
});
console.log('File count: ' + this.state.files.length);
// You might check that the "event" has a file & assign it like this
// in vanilla Javascript:
// var files = event.target.files;
// if (!files && files.length > 0)
// files = (event.dataTransfer ? event.dataTransfer.files :
// event.originalEvent.dataTransfer.files);
// You cannot use "files" as a variable in React, however:
const in_files = this.state.files;
// iterate, if files length > 0
if (in_files.length > 0) {
for (let i = 0; i < in_files.length; i++) {
// use this, instead, for vanilla JS:
// for (var i = 0; i < files.length; i++) {
const a = i + 1;
console.log('in loop, pass: ' + a);
const f = in_files[i]; // or just files[i] in vanilla JS
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 就会为您将 File 对象放入 this.state.files。我从该帖子的答案中添加了有关如何获取 File 对象的语法。它应该可以工作,或者那里有其他帖子可以提供帮助。但 Q/A 告诉我的只是如何获取 File 对象,而不是 blob 数据本身。即使我在 sebu 的答案中做了fileData = new Blob([files[0]]);,由于某种原因没有包含var,它也没有告诉我如何读取该 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; // no operation
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 = '';
// Percent loaded in string
if (loaded >= 0 && loaded < 10) {
zeros = '00';
}
else if (loaded < 100) {
zeros = '0';
}
// Display progress in 3-digits and increase bar length
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 中提取的变量中。因此,我将其直接设置为变量attachments,它位于我的JSON 对象RequestForm 中——与我的this.state 使用的对象相同。 attachments 是一个数组,所以我可以推送多个文件。事情是这样的:
const fileArray = [];
// Collect any existing attachments
if (RequestForm.state.attachments.length > 0) {
for (let i=0; i < RequestForm.state.attachments.length; i++) {
fileArray.push(RequestForm.state.attachments[i]);
}
}
// Add the new one to this.state
fileArray.push(bytes);
// Update the state
RequestForm.setState({
attachments: fileArray,
});
那么,因为this.state 已经包含RequestForm:
this.stores = [
RequestForm,
]
从那以后我可以将其称为this.state.attachments。不适用于 vanilla JS 的 React 功能。你可以用一个全局变量在纯 JavaScript 中构建一个类似的结构,然后相应地推送,但是要容易得多:
var fileArray = new Array(); // place at the top, before any functions
// Within your processFile():
var newFileArray = [];
if (fileArray.length > 0) {
for (var i=0; i < fileArray.length; i++) {
newFileArray.push(fileArray[i]);
}
}
// Add the new one
newFileArray.push(bytes);
// Now update the global variable
fileArray = newFileArray;
然后你总是只引用fileArray,为任何文件字节字符串枚举它,例如var myBytes = fileArray[0]; 第一个文件。