Dosya nasıl sürükleyip bırakılır?

HTML sürükle ve bırakma arayüzleri, web uygulamalarının bir web sayfasında sürüklenip bırakılan dosyaları kabul etmesini sağlar. Sürükleyip bırakma işlemi sırasında, sürüklenen dosya ve dizin öğeleri sırasıyla dosya girişleriyle ve dizin girişleriyle ilişkilendirilir. Dosyaları tarayıcıya sürükleyip bırakma konusunda iki yöntem vardır: modern ve klasik yöntem.

Modern yöntem

File System Access API'nin DataTransferItem.getAsFileSystemHandle() yöntemini kullanma

DataTransferItem.getAsFileSystemHandle() yöntemi, sürüklenen öğe bir dosyaysa FileSystemFileHandle nesnesi içeren bir söz, sürüklenen öğe bir dizinse FileSystemDirectoryHandle nesnesi içeren bir söz döndürür. Bu tutamaçlar, dosyayı veya dizini okumanıza ve isteğe bağlı olarak dosyaya ya da dizine geri yazmanıza olanak tanır. Sürükle ve bırak arayüzünün DataTransferItem.kind simgesinin hem dosyalar hem de dizinler için "file", File System Access API'nin FileSystemHandle.kind simgesinin ise dosyalar için "file", dizinler için "directory" olacağını unutmayın.

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: not supported.
  • Safari: not supported.

Source

Klasik yöntem

Klasik DataTransferItem.getAsFile() yöntemini kullanma

DataTransferItem.getAsFile() yöntemi, sürükleme verisi öğesinin File nesnesini döndürür. Öğe dosya değilse bu yöntem null değerini döndürür. Dosyayı okuyabilirsiniz ancak dosyaya geri yazamazsınız. Bu yöntemin dizinleri desteklememesi dezavantajıdır.

Browser Support

  • Chrome: 11.
  • Edge: 12.
  • Firefox: 50.
  • Safari: 5.1.

Source

Progresif geliştirme

Aşağıdaki snippet, desteklendiğinde modern File System Access API'nin DataTransferItem.getAsFileSystemHandle() yöntemini kullanır, ardından standart olmayan DataTransferItem.webkitGetAsEntry() yöntemine ve son olarak klasik DataTransferItem.getAsFile() yöntemine geri döner. Her bir handle öğesinin türünü kontrol etmeyi unutmayın. Bu türler şunlardan biri olabilir:

  • FileSystemFileHandle modern kod yolu seçildiğinde.
  • File klasik kod yolu seçildiğinde.

Tüm türlerde name özelliği bulunur. Bu nedenle, bu özelliği günlüğe kaydetmek sorunsuz bir şekilde çalışır.

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

Daha fazla bilgi

Demo

HTML

<!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>

CSS


        :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;
}
        

JS


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