如何使用 Javascript 将文件添加到已经存在的 dataTransfer 对象中

Posted

技术标签:

【中文标题】如何使用 Javascript 将文件添加到已经存在的 dataTransfer 对象中【英文标题】:How to add a file into an already existing dataTransfer object using Javascript 【发布时间】:2014-08-21 05:30:52 【问题描述】:

假设:本地 html/javascript 网页可以访问 file://

在可拖动的 HTML 元素上开始拖动时,在事件处理函数 dragStart(e) 中,如何添加 File 对象以便将其识别为文件并最终出现在 dataTransfer.files 列表中?

例如:

function dragStart(e)
    var file = getSomeFileObjFromSomewhere();
    e.originalEvent.dataTransfer.effectAllowed = "all";
    e.originalEvent.dataTransfer.setData("file", file);

    console.log("\nFiles:");
    i = 0;
    var files = e.originalEvent.dataTransfer.files,
    len = files.length;
    for (; i < len; i++) 
        console.log("\nIndex: " + i + "\nFilename: " + files[i].name);
        console.log("Type: " + files[i].type);
        console.log("Size: " + files[i].size + " bytes");
        console.dir(files[i]);
    

具体来说,它需要在 Chrome/Chromium 上运行。而且,我们可以假设该文件存在于本地驱动器上。基本上,当文件从 Windows 资源管理器拖动到可放置元素上的 HTML 页面时,我希望获得相同的数据。

我知道这在 Chrome 中存在:

e.originalEvent.dataTransfer.setData("DownloadURL", fileType + ":" + name + ":" + filePath);

下载文件。但这不是我想要的,因为我想假设这是一个现有文件并且必须访问原始文件。

【问题讨论】:

我怀疑这可以做到。如果我理解正确,您实际上是在要求浏览器上传某人的文件,而无需他们启动文件传输。我看不到允许这种情况发生的浏览器 - 这将是一个安全漏洞 如果您能够在var file = getSomeFileObjFromSomewhere(); 获取文件,那么在event.dataTransfer 设置文件对象的目的是什么?你想达到什么目的? @KScandrett - File 对象必须从 Javascript 已经可以访问的东西生成(包括通过用户与文件输入交互显式生成的 File 对象) 我正在尝试从一个页面生成一个文件,用户可以将其拖到另一个页面,就像他们将文件从文件系统拖到另一个页面一样 “另一页”是什么意思? Drag and drop images, and not links, between windows - HTML5? 【参考方案1】:

您可以使用@kol 在Simulate drop file event 发布的方法

也就是说,我们必须向 ondrop 传递一个参数,它

有一个dataTransfer 字段和一个files 数组子字段,其中包含选定的File,并且 preventDefault 方法(没有主体的函数可以)。

在下面进行了调整,以在 dragstart 事件处将 .addEventListener("drop") 附加到 drop 元素,将 File 对象传递给带有 Function.prototype.bind() 的绑定函数,该函数返回上述适当的对象,once:true 在第三个传递.addEventListener() 的参数,对于访问或创建 File 对象的每个 dragstart 事件最多调用一次 drop 事件。

FileList 对象是只读的,Array 用于将File 对象的dataTransfer.files 属性存储在事件处理程序的普通javascript 对象中。

注意:FileList 接口应被视为“有风险” 因为网络平台上的大趋势是取代这样的 与 ECMAScript [ECMA-262] 中的Array 平台对象接口。特别是,这意味着排序的语法 filelist.item(0) 有风险;大多数其他程序化使用 FileList 不太可能受到最终迁移到 Array 类型。

如果event.dataTransfer.files at dragstart 事件包含File 对象,则迭代FileList 并将每个File 对象推送到files 数组。

var drag = document.getElementById("drag")
var drop = document.getElementById("drop")

function handleDrop(evt) 
  evt.preventDefault();
  console.log(evt.dataTransfer.files);


function getSomeFileObjFromSomewhere() 
  var data = ["abc", "def"];
  var files = [];
  for (var i = 0; i < data.length; i++) 
    files.push(new File([data[i]], data[i] + ".text", 
      type: "text/plain",
      lastModified: new Date().getTime()
    ));
  
  return files


function dataTransferFileObject(files) 
  return 
    preventDefault: function() ,
    dataTransfer: 
      files: Array.isArray(files) ? files : [files]
    
  


drag.addEventListener("dragstart", function dragStart(e) 

  var files = getSomeFileObjFromSomewhere();
  e.dataTransfer.effectAllowed = "all";

  console.log("\nFiles:");
  
  for (let i = 0; i < files.length; i++) 
    var name, size, type = files[i];
    console.log("\nFilename: " + name);
    console.log("Type: " + type);
    console.log("Size: " + size + " bytes");
  
  // if `e.dataTransfer.files`, push `File` objects dragged
  // to `files` array
  if (e.dataTransfer.files) 
    for (let file of e.dataTransfer.files) 
      files.push(file);
    
  
  
  drop.addEventListener("drop"
  , handleDrop.bind(drop, dataTransferFileObject(files))
  , once: true);

);

drop.addEventListener("dragover", function(evt) 
  evt.preventDefault()
);
div 
  width: 50px;
  height: 50px;
  padding: 10px;
  margin: 10px;


div:nth-child(1) 
  border: 2px dotted blue;


div:nth-child(2) 
  border: 2px dotted green;
<div draggable="true" id="drag">drag</div>
<div droppable="true" id="drop" webkitdropzone="webkitdropzone">drop</div>

plnkrhttp://plnkr.co/edit/ihQqs4t2zOg2XhIuNwal?p=preview

【讨论】:

谢谢,这似乎是一个进步!从 dragstart 事件中查看时,它似乎可以工作,但 dataTransfer 项目/文件似乎不会持续到 drop 事件。还是我在那里做错了什么? plnkr.co/edit/H5gfm82JZGRuEuDIM7WM?p=preview @FabioBeltramini 注意,该问题实际上并未提及drop 事件。 好的,关于 drop 事件,也许我对这句话读得太多了“我希望在将文件从 Windows 资源管理器拖到 HTML 页面上的元素上时获得相同的数据 [as]可丢弃”。但是让我们看看这个新的 plkr 是否有帮助 新 pluker 上的 drop listener 没有检查 dataTransfer 文件。似乎没有办法将文件附加到 dataTransfer 以便 drop 事件可以将它们作为文件接收? 您可以在 setData 中传递数据 URI,并在 drop 事件中使用 getData。虽然 FileReader 加载事件异步返回结果。【参考方案2】:

最简单的方法是“添加项目”:

const dataTransfer = new DataTransfer();
const aFileParts = ['<a id="a"><b id="b">hey!</b></a>'];
dataTransfer.items.add(new File([new Blob(aFileParts,  type: 'text/html' )], 'test.txt'));

只有你需要它来调整你的文件到文件类型:)

【讨论】:

aFileParts 在这段代码中的作用是什么?我不明白它的功能...... 这只是一个模拟,因为new Blob() 需要这个参数。你可以在那里创建任何你想要的东西:) 是的,这是真的。在我的例子中,它是一个“已经创建”的 blob,所以我只需要在 newFile() 中传递它。

以上是关于如何使用 Javascript 将文件添加到已经存在的 dataTransfer 对象中的主要内容,如果未能解决你的问题,请参考以下文章

如何从暂存区域中删除单个文件(撤消 git add)?

如何将 TypeScript 定义文件添加到源代码管理?

如何将滚动窗格内容保存为 jpg 文件

NPM - 暂存文件未添加到使用 npm 版本提交

JavaScript:如何将数据添加到 JSON 文件中的数组

在 git 中暂存文件?