Dateien per Drag-and-drop verschieben

Thomas Steiner
Thomas Steiner

Mit den HTML-Drag-and-Drop-Oberflächen können Webanwendungen Drag-and-drop-Dateien auf einer Webseite akzeptieren. Während eines Drag-and-drop-Vorgangs werden per Drag-and-drop verschobene Datei- und Verzeichniselemente Datei- bzw. Verzeichniseinträgen zugeordnet. Es gibt zwei Möglichkeiten, Dateien per Drag-and-drop in den Browser zu ziehen: die moderne und die klassische.

Die Methode DataTransferItem.getAsFileSystemHandle() gibt ein Promise mit einem FileSystemFileHandle-Objekt zurück, wenn das gezogene Element eine Datei ist, und ein Promise mit einem FileSystemDirectoryHandle-Objekt, wenn das gezogene Element ein Verzeichnis ist. Mit diesen Handles können Sie die Datei oder das Verzeichnis lesen und optional zurückschreiben. Der DataTransferItem.kind der Drag-and-drop-Oberfläche ist "file" für Dateien und Verzeichnisse. FileSystemHandle.kind der File System Access API ist "file" für Dateien und "directory" für Verzeichnisse.

Unterstützte Browser

  • 86
  • 86
  • x
  • x

Quelle

Die klassische Art

Mit der klassischen DataTransferItem.getAsFile()-Methode

Die Methode DataTransferItem.getAsFile() gibt das File-Objekt des gezogenen Datenelements zurück. Wenn das Element keine Datei ist, gibt diese Methode null zurück. Sie können die Datei zwar lesen, aber nicht zurückschreiben. Diese Methode hat den Nachteil, dass sie keine Verzeichnisse unterstützt.

Unterstützte Browser

  • 11
  • 12
  • 50
  • 5.1

Quelle

Progressive Enhancement

Im folgenden Snippet wird die DataTransferItem.getAsFileSystemHandle()-Methode der modernen File System Access API verwendet, sofern sie unterstützt wird. Dann wird auf die nicht standardmäßige DataTransferItem.webkitGetAsEntry()-Methode zurückgesetzt und schließlich wird auf die klassische DataTransferItem.getAsFile()-Methode zurückgesetzt. Prüfen Sie den Typ jedes handle-Elements, da es sich um einen der folgenden Werte handeln kann:

  • FileSystemFileHandle, wenn der moderne Codepfad ausgewählt wird.
  • File, wenn der klassische Codepfad ausgewählt wird.

Alle Typen haben ein name-Attribut. Die Protokollierung ist also kein Problem und funktioniert immer.

// Run feature detection.
const supportsFileSystemAccessAPI =
 
'getAsFileSystemHandle' in DataTransferItem.prototype;

// This is the drag and drop zone.
const elem = document.querySelector('main');

 
// Prevent navigation.
elem
.addEventListener('dragover', (e) => {
  e
.preventDefault();
});

// Visually highlight the drop zone.
elem
.addEventListener('dragenter', (e) => {
  elem
.style.outline = 'solid red 1px';
});

// Visually unhighlight the drop zone.
elem
.addEventListener('dragleave', (e) => {
  elem
.style.outline = '';
});

// This is where the drop is handled.
elem
.addEventListener('drop', async (e) => {
 
// Prevent navigation.
  e
.preventDefault();
 
// Unhighlight the drop zone.
  elem
.style.outline = '';
 
// Prepare an array of promises…
 
const fileHandlesPromises = [...e.dataTransfer.items]
   
// …by including only files (where file misleadingly means actual file _or_
   
// directory)…
   
.filter((item) => item.kind === 'file')
   
// …and, depending on previous feature detection…
   
.map((item) =>
      supportsFileSystemAccessAPI
       
// …either get a modern `FileSystemHandle`…
       
? item.getAsFileSystemHandle()
       
// …or a classic `File`.
       
: item.getAsFile(),
   
);
 
// Loop over the array of promises.
 
for await (const handle of fileHandlesPromises) {
   
// This is where we can actually exclusively act on the files.
   
if (handle.kind === 'file' || handle.isFile) {
      console
.log(`File: ${handle.name}`);
   
}
 
}
});

Weitere Informationen

Demo

<!DOCTYPE html>
<html lang="en">
 
<head>
   
<meta charset="utf-8" />
   
<meta name="viewport" content="width=device-width, initial-scale=1" />
   
<title>How to drag and drop files</title>
 
</head>
 
<body>
   
<main>
     
<h1>How to drag and drop files</h1>
     
<p>Drag and drop one or multiple files onto the page.</p>
     
<pre></pre>
   
</main>
 
</body>
</html>

       
:root {
 
color-scheme: dark light;
 
box-sizing: border-box;
}

*,
*:before,
*:after {
 
box-sizing: inherit;
}

body
{
 
margin: 0;
 
padding: 1rem;
 
font-family: system-ui, sans-serif;
 
line-height: 1.5;
 
min-height: 100vh;
 
display: flex;
 
flex-direction: column;
}

img
,
video
{
 
height: auto;
 
max-width: 100%;
}

main
{
 
flex-grow: 1;
}

footer
{
 
margin-top: 1rem;
 
border-top: solid CanvasText 1px;
 
font-size: 0.8rem;
}
       

       
const supportsFileSystemAccessAPI =
 
"getAsFileSystemHandle" in DataTransferItem.prototype;
const supportsWebkitGetAsEntry =
 
"webkitGetAsEntry" in DataTransferItem.prototype;

const elem = document.querySelector("main");
const debug = document.querySelector("pre");

elem
.addEventListener("dragover", (e) => {
 
// Prevent navigation.
  e
.preventDefault();
});

elem
.addEventListener("dragenter", (e) => {
  elem
.style.outline = "solid red 1px";
});

elem
.addEventListener("dragleave", (e) => {
  elem
.style.outline = "";
});

elem
.addEventListener("drop", async (e) => {
  e
.preventDefault();
  elem
.style.outline = "";
 
const fileHandlesPromises = [...e.dataTransfer.items]
   
.filter((item) => item.kind === "file")
   
.map((item) =>
      supportsFileSystemAccessAPI
       
? item.getAsFileSystemHandle()
       
: supportsWebkitGetAsEntry
       
? item.webkitGetAsEntry()
       
: item.getAsFile()
   
);

 
for await (const handle of fileHandlesPromises) {
   
if (handle.kind === "directory" || handle.isDirectory) {
      console
.log(`Directory: ${handle.name}`);
      debug
.textContent += `Directory: ${handle.name}\n`;
   
} else {
      console
.log(`File: ${handle.name}`);
      debug
.textContent += `File: ${handle.name}\n`;
   
}
 
}
});