Glisser-déposer des fichiers

Les interfaces de glisser-déposer HTML permettent aux applications Web d'accepter les fichiers glissés-déposés sur une page Web. Lors d'une opération de glisser-déposer, les éléments de fichier et de répertoire glissés sont associés respectivement aux entrées de fichier et de répertoire. Pour glisser-déposer des fichiers dans le navigateur, vous avez le choix entre deux méthodes: la méthode moderne et la méthode classique.

La méthode DataTransferItem.getAsFileSystemHandle() renvoie une promesse avec un objet FileSystemFileHandle si l'élément glissé est un fichier et une promesse avec un objet FileSystemDirectoryHandle si l'élément glissé est un répertoire. Ces identifiants vous permettent de lire et éventuellement d'écrire dans le fichier ou le répertoire. Notez que la méthode DataTransferItem.kind de l'interface de glisser-déposer sera "file" pour les fichiers et les répertoires, tandis que la valeur FileSystemHandle.kind de l'API File System Access sera "file" pour les fichiers et "directory" pour les répertoires.

Navigateurs pris en charge

  • 86
  • 86
  • x
  • x

Source

Méthode classique

Utiliser la méthode DataTransferItem.getAsFile() classique

La méthode DataTransferItem.getAsFile() renvoie l'objet File de l'élément de données de déplacement. Si l'élément n'est pas un fichier, cette méthode renvoie null. Bien que vous puissiez lire le fichier, il n'existe aucun moyen d'y réécrire. Cette méthode a l'inconvénient de ne pas être compatible avec les répertoires.

Navigateurs pris en charge

  • 11
  • 12
  • 50
  • 5.1

Source

amélioration progressive

L'extrait ci-dessous utilise la méthode DataTransferItem.getAsFileSystemHandle() de l'API File System Access moderne lorsqu'elle est compatible, puis revient à la méthode DataTransferItem.webkitGetAsEntry() non standard et revient enfin à la méthode DataTransferItem.getAsFile() classique. Veillez à vérifier le type de chaque handle, car il peut s'agir de l'un des éléments suivants:

  • FileSystemFileHandle lorsque le chemin de code moderne est choisi.
  • File lorsque le chemin de code classique est choisi.

Tous les types ont une propriété name. Leur journalisation est donc acceptable et fonctionnera toujours.

// 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}`);
   
}
 
}
});

Complément d'informations

Démonstration

<!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`;
   
}
 
}
});