How to add a file into an already existing dataTransfer object using Javascript
Asked Answered
S

3

23

Assumption: A local HTML/Javascript webpage that has access to file://

At the start of a drag on a draggable HTML element, in the event handler function dragStart(e), how do I add a File object so that it is recognized as a file and ends up in the dataTransfer.files list?

Ex:

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]);
    }
}

Specifically, it needs to work on Chrome/Chromium. And, we can assume that the file exists on the local drive. Basically, I want the same data available then when a file is dragged from Windows Explorer to a HTML page on an element that is droppable.

I know that this exists in Chrome:

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

which downloads the file. But this is not what I want, because I want to assume that this is an existing file and that the original file must be accessed.

Su answered 30/6, 2014 at 18:55 Comment(10)
I doubt this can be done. If I understand correctly you're essentially asking the browser to upload someone's file without them initiating the file transfer. I can't see browsers allowing that to happen - it would be a security flawContemn
If you are able to get file at var file = getSomeFileObjFromSomewhere(); what is purpose of setting file object at event.dataTransfer? What are you trying to achieve?Dugan
@KScandrett - the File object would have to be generated from things already accessible to Javascript (including File objects generated explicitly by user interaction with a file input)Witham
I am trying to achieve generating a file from one page that can be dragged by the user into another page, like they would drag a file from their file system into that other pageWitham
What do you mean by "another page"? Drag and drop images, and not links, between windows - HTML5?Dugan
"like they would drag a file from their file system into that other page" Do you mean drag and drop between two html documents? Or drag a file from user file manager UI at OS to an html document?Dugan
@FabioBeltramini Can you provide further description as to what are you trying to achieve?Dugan
From a dragstart event in one html page/document/window, attach a JS-generated file-object to the event in such a way that the user can drop it into another window, and that window recognizes the attached file, just as if the user had dragged it from their file systemWitham
In my scenario, the target page exists and listens for file drops, I can't edit it, I just want to make a tool for building and passing in compatible filesWitham
Example scenario: generate a CSV file using the "File" object, and store it in the drag object, as if the user dragged a file from their filesystem. This is useful to unify some workflows.Rsfsr
D
6

You can use the approach posted by @kol at Simulate drop file event

That is, we have to pass an argument to ondrop, which

  • has a dataTransfer field with a files array subfield, which contains the selected File, and
  • a preventDefault method (a function with no body will do).

adjusted below to attach .addEventListener("drop") to drop element at dragstart event, with File objects passed to a bound function with Function.prototype.bind() which returns the appropriate object described above, with once:true passed at third parameter to .addEventListener(), to call drop event at most once for each dragstart event where File objects are accessed or created.

FileList object is read only, an Array is used to store File object at dataTransfer.files property within a plain javascript object at event handlers.

Note: The FileList interface should be considered "at risk" since the general trend on the Web Platform is to replace such interfaces with the Array platform object in ECMAScript [ECMA-262]. In particular, this means syntax of the sort filelist.item(0) is at risk; most other programmatic use of FileList is unlikely to be affected by the eventual migration to an Array type.

If event.dataTransfer.files at dragstart event contains File objects, iterate FileList and push each File object to files array.

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>

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

Dugan answered 2/3, 2017 at 17:39 Comment(9)
Thanks, this seems like progress! It appears to work when viewed from within the dragstart event, but the dataTransfer items/files don't seem to persist through to the drop event. Or am I doing something wrong there? plnkr.co/edit/H5gfm82JZGRuEuDIM7WM?p=previewWitham
@FabioBeltramini Note, the Question does not actually mention drop event.Dugan
Ok, regarding the drop event, perhaps I read too much into this phrase "I want the same data available [as] when a file is dragged from Windows Explorer to a HTML page on an element that is droppable". But lets see if this new plkr helpsWitham
The drop listener on the new pluker isn't checking the dataTransfer files. It's seeming like there is no way to attach files to the dataTransfer such that the drop event can receive them as files?Witham
You can pass a data URI at setData and use getData at drop event. Though FileReader load event returns results asynchronously.Dugan
I will award you the bounty (as the only answer). Unfortunately, clever as this may be, it still doesn't help me, as I cannot modify the drop listener because it is on another page.Witham
@FabioBeltramini "Note: Dragging files can currently only happen from outside a browsing context, for example from a file system manager application." 7.7.5 Drag-and-drop processing model. Though you can use .setData(), .getData() approach to transfer data URI representation of file, see console at plnkr.co/edit/zmyRoA11W4iMl7m8TIPS?p=preview. Note also, approach at stackoverflow.com/a/41388593Dugan
@FabioBeltramini Have you considered posting a new Question including full context and requirements described at #24497489, #24497489 , #24497489 ?Dugan
Yes, I was planning on doing so once this bounty expired. However, your link to w3 helps to establish that the answer to my question would be "currently not possible". Thank you!Witham
T
6

The simplest way is by "add item":

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'));

Only what u need it's adjust your file to file type :)

Tunicle answered 25/11, 2021 at 10:43 Comment(4)
What the constant aFileParts is doing in this code ? I don't understand its functionnality...Atlantic
It is just a mock because new Blob() requires this param. You can create there whatever you want :)Tunicle
Yes, that's true. In my case, it was an "already created" blob so I just had to pass it in newFile().Atlantic
Then dataTransfer.types is [text/plain], while when dragging a real file from the file browser it is [Files].Rsfsr
G
0

You cannot add a file to DataTransferItemList on Chromium. And it's not even treated as a bug. Very sneaky. https://issues.chromium.org/issues/40853586

<html>
<body>
    <div draggable="true" id="x" style="width:100px;height:100px;background:black;color:white">DRAG THIS</div>
    <script>
        const x = document.getElementById('x');

        x.addEventListener('dragstart', e => {
            const i = e.dataTransfer.items.add(new File(['hello'], 'hello.txt', { type: 'text/plain' }));
            console.log(`[dragstart] Added ${i.kind} of type ${i.type}`);
        });

        document.addEventListener('dragover', e => {
            e.preventDefault();
        });

        document.addEventListener('drop', e => {
            e.preventDefault();

            for (const item of e.dataTransfer.items) {
                const f = item.getAsFile();

                if (f) {
                    console.log(`[drop] Received file of type ${item.type}`);
                } else {
                    item.getAsString(data => {
                        console.log(`[drop] Received string: ${data}`);
                    });
                }
            }
        })
    </script>
</body>
</html>
Glassco answered 11/6, 2024 at 15:48 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.