Dizinleri nasıl sürükleyip bırakabilirsiniz?

Thomas Steiner'ın yer aldığı daha fazla içerik
Thomas Steiner

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

Modern yöntem

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

DataTransferItem.getAsFileSystemHandle() yöntemi, sürüklenen öğe bir dosyaysa FileSystemFileHandle nesnesiyle taahhüt, sürüklenen öğe bir dizinse FileSystemDirectoryHandle nesnesiyle taahhüt döndürür. Bu tutma yerlerini kullanarak dosyayı veya dizini okuyabilir ve isteğe bağlı olarak tekrar yazabilirsiniz. Sürükle ve Bırak arayüzünün DataTransferItem.kind hem dosyalar hem de dizinler için "file" olurken, Dosya Sistemi Erişim API'si FileSystemHandle.kind dosyalar için "file", dizinler için ise "directory" olacaktır.

Tarayıcı Desteği

  • 86
  • 86
  • x
  • x

Kaynak

Klasik yöntem

Standart olmayan DataTransferItem.webkitGetAsEntry() yöntemi kullanılıyor

DataTransferItem.webkitGetAsEntry() yöntemi, öğe bir dosyaysa sürükleme veri öğesinin FileSystemFileEntry değerini ve öğe bir dizinse FileSystemDirectoryEntry değerini döndürür. Dosya veya dizinleri okuyabiliyor olsanız da bunlara yanıt yazamazsınız. Bu yöntemin dezavantajı, standartlar kanalında yer almaması fakat dizinleri desteklemesi gibi bir avantajıdır.

Tarayıcı Desteği

  • 13
  • 14
  • 50
  • 11.1

Kaynak

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 geri döner ve son olarak klasik DataTransferItem.getAsFile() yöntemine geri döner. Aşağıdaki durumlardan biri olabileceği için her handle türünü kontrol ettiğinizden emin olun:

  • Modern kod yolu seçildiğinde FileSystemDirectoryHandle.
  • Standart olmayan kod yolu seçildiğinde FileSystemDirectoryEntry.

Tüm türlerin bir name özelliği vardır. Bu nedenle, giriş yapmak sorun yaratmaz ve her zaman çalışır.

// Run feature detection.
const supportsFileSystemAccessAPI =
  'getAsFileSystemHandle' in DataTransferItem.prototype;
const supportsWebkitGetAsEntry =
  'webkitGetAsEntry' 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();
  if (!supportsFileSystemAccessAPI && !supportsWebkitGetAsEntry) {
    // Cannot handle directories.
    return;
  }
  // 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 `FileSystemFileEntry`.
        : item.webkitGetAsEntry(),
    );
  // Loop over the array of promises.
  for await (const handle of fileHandlesPromises) {
    // This is where we can actually exclusively act on the directories.
    if (handle.kind === 'directory' || handle.isDirectory) {
      console.log(`Directory: ${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 directories</title>
  </head>
  <body>
    <main>
      <h1>How to drag and drop directories</h1>
      <p>Drag and drop one or multiple files or directories 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`;
    }
  }
});