将多个文件拖放到浏览器中仅适用于Firefox - 不适用于Chrome或Edge。



function allowDropFiles(event) {
event.preventDefault();
}
function readFile(file) {
return new Promise((resolve, reject) => {
var fr = new FileReader();
fr.onload = () => {
resolve(fr.result)
};
fr.onerror = reject;
fr.readAsDataURL(file);
});
}
async function DropFiles(event) {
event.preventDefault();
var vVoucherArray = [];
var vFileNameArray = [];
var vFileContentArray = [];
voucherNo = event.target.id.substring(4, event.target.id.length);
if (event.dataTransfer.items) {
for (var i = 0; i < event.dataTransfer.items.length; i++) {
if (event.dataTransfer.items[i].kind === "file") {
const file = event.dataTransfer.items[i].getAsFile();
if (file != null) {
vVoucherArray.push(voucherNo);
vFileNameArray.push(file.name);
vFileContentArray.push(await readFile(file));
}
}
}
}
else {
for (var i = 0; i < event.dataTransfer.files.length; i++) {
const file = event.dataTransfer.files[i];
vVoucherArray.push(voucherNo);
vFileNameArray.push(file.name);
vFileContentArray.push(await readFile(file));
}
}
}
<div id="div0" ondrop="DropFiles(event)" ondragover="allowDropFiles(event)" style="height:100px; width:100px; background-color:red" title="Drop bilag her">

我对下面的代码有一个奇怪的问题。我的目标是通过Javascript一次上传多个文件到网站。它在Firefox中运行得很好,但在Chrome和Microsoft Edge中则不然。

Chrome和Microsoft Edge中的错误消息是:;加载资源失败:服务器的响应状态为404((";

我可以看到event.dataTransfer.items.length有合适数量的文件,我想上传-但Chrome/Edge只上传第一个文件,然后停止工作:-(

所以我的问题是:这是这两个浏览器中的错误吗?还是我需要用不同的代码来完成这项任务?

感谢您的帮助。

问候Michael

function readFile(file) {
return new Promise((resolve, reject) => {
var fr = new FileReader();
fr.onload = () => {
resolve(fr.result)
};
fr.onerror = reject;
fr.readAsDataURL(file);
});
}
async function DropFiles(event) {
event.preventDefault();
var vVoucherArray = [];
var vFileNameArray = [];
var vFileContentArray = [];
voucherNo = event.target.id.substring(4, event.target.id.length);
if (event.dataTransfer.items) {
for (var i = 0; i < event.dataTransfer.items.length; i++) {
if (event.dataTransfer.items[i].kind === "file") {
const file = event.dataTransfer.items[i].getAsFile();
if (file != null) {
vVoucherArray.push(voucherNo);
vFileNameArray.push(file.name);
vFileContentArray.push(await readFile(file));
}
}
}
doUploadFile(vVoucherArray, vFileNameArray, vFileContentArray);
}
}

将多个文件拖放到Chrome或Edge中不起作用的浏览器中是一个错误*

发布的代码的情况是,当事件处理代码返回到事件循环时,除了将dataTranfer对象的属性值替换为空列表对象外,还将event.dataTransfer.itemsevent.dataTransfer.files的长度设置为零,从而破坏它们。为了完成销毁,删除了在恢复事件处理代码时对项目调用getAsFile的功能。总的来说,在恢复执行后,事件处理程序成功地使对拖放数据存储的访问变得不可能。

这种情况发生在WebKit浏览器中(Edge测试了这个答案(,但在Firefox中没有。返回事件循环是在执行await运算符期间发生的,特别是在计算await readFile(file)时。

在WebKit下,当await恢复for循环时,循环条件为false,因为i为1,dataTransfer.items.lengthdataTransfer.files.length都为0,导致循环在第一次迭代结束时中断。

为什么

想到这样做的主要潜在原因是在返回事件循环后使window.event无效。尽管现在完全不推荐使用window.event,而不是传递给事件句柄的event对象参数值,这是早期版本的Microsoft IE的工作方式。在现代浏览器中,这种使用不再是线程安全的,因为await运算符现在可以使用相同的执行上下文中断和恢复JavaScript执行。

另一个问题可能是对所有拖放操作使用单一的静态数据存储——如果这种情况下,整个拖放设计将无法重新进入。

解决方案

由于事件对象中原始files和/或items列表的长度被设置为零,因此在尝试读取文件对象之前复制文件对象提供了一种解决方案。以下是我用来探索并使其发挥作用的片段:

"use strict";
function allowDropFiles(event) {
event.preventDefault();
}
async function DropFiles(event) {
event.preventDefault();
var files = [];
var vVoucherArray = [];
var vFileNameArray = [];
var vFileContentArray = [];
var voucherNo = event.target.id.substring(4, event.target.id.length);
const items = event.dataTransfer.items;
if (items) {
console.log( "items: ", items.length);
for (var i = 0; i < items.length; i++) {
if( items[i].kind === 'file') {
files.push(items[i].getAsFile());
}
}
console.log( files);
}
for( var i = 0; i < files.length; ++i) {
var file = files[i];
if (file) {
vVoucherArray.push(voucherNo);
vFileNameArray.push(file.name);
vFileContentArray.push(await readFile(file));
}
}
doUploadFile(vVoucherArray, vFileNameArray, vFileContentArray);
}
function readFile(file) {
return new Promise((resolve, reject) => {
var fr = new FileReader();
fr.onload = () => {
resolve(fr.result)
};
fr.onerror = reject;
fr.readAsDataURL(file);
});
}
function doUploadFile(vouchers, fileNames, fileContent){
console.log(vouchers,fileNames);
var total =0;
fileContent.forEach(text=>total+= text.length);
console.log("Total bytes: " + total);
}
<div id="div0" ondrop="DropFiles(event)" ondragover="allowDropFiles(event)" style="height:100px; width:100px; background-color:red" title="Drop bilag her">

该片段一直保持在帖子附近,并遍历items数组,检查kind==="file"以筛选条目。一个更简单的选项可能是使用以下等效项克隆文件列表:

const files = Array.from(event.dataTransfer.files);

*这真的是个bug吗

我称之为bug,通过确保它在有疑问时会失败来解决可重入性问题。供应商可能不同意——暗示dataTranfer对象可以与HTML标准中的拖拽数据存储区分离是另一种说法,即这是有文档记录的行为,如果开发人员被它咬了,这不是他们的问题(IMHO(。

不要使用readFile()等。使用formData,如:

function allowDropFiles(event) {
event.preventDefault();
}
let formData = new FormData();
async function DropFiles(event) {
event.preventDefault();
if (event.dataTransfer.items) {
for (var i = 0; i < event.dataTransfer.items.length; i++) {
if (event.dataTransfer.items[i].kind === "file") {
const file = event.dataTransfer.items[i].getAsFile();
if (file != null) {
formData.append("file", file);
}
}
}
}
for (const value of formData.values()) {
console.log(value);
}
}
<div id="div0" ondrop="DropFiles(event)" ondragover="allowDropFiles(event)" style="height:100px; width:100px; background-color:red" title="Drop bilag her">

参考:

  • 表单数据

最新更新