نحوه کشیدن و رها کردن فایل ها

رابط‌های کشیدن و رها کردن HTML به برنامه‌های وب این امکان را می‌دهند که فایل‌های کشیده شده و رها شده را در یک صفحه وب بپذیرند. در طول عملیات کشیدن و رها کردن، آیتم‌های فایل و دایرکتوری کشیده شده به ترتیب با ورودی‌های فایل و ورودی‌های دایرکتوری مرتبط می‌شوند. وقتی صحبت از کشیدن و رها کردن فایل‌ها در مرورگر می‌شود، دو روش برای انجام آن وجود دارد: روش مدرن و کلاسیک.

روش مدرن

استفاده از متد DataTransferItem.getAsFileSystemHandle() در رابط برنامه‌نویسی کاربردی دسترسی به سیستم فایل

متد DataTransferItem.getAsFileSystemHandle() اگر آیتم کشیده شده یک فایل باشد، یک promise با شیء FileSystemFileHandle و اگر آیتم کشیده شده یک دایرکتوری باشد، یک promise با شیء FileSystemDirectoryHandle برمی‌گرداند. این handleها به شما امکان می‌دهند فایل یا دایرکتوری را بخوانید و به صورت اختیاری در آن بنویسید. توجه داشته باشید که DataTransferItem.kind رابط Drag and Drop برای فایل‌ها و دایرکتوری‌ها "file" خواهد بود، در حالی که FileSystemHandle.kind File System Access API برای فایل‌ها "file" و برای دایرکتوری‌ها "directory" خواهد بود.

Browser Support

  • کروم: ۸۶.
  • لبه: ۸۶.
  • فایرفاکس: پشتیبانی نمی‌شود.
  • سافاری: پشتیبانی نمی‌شود.

Source

روش کلاسیک

استفاده از متد کلاسیک DataTransferItem.getAsFile()

متد DataTransferItem.getAsFile() شیء File مربوط به آیتم داده‌ی drag شده را برمی‌گرداند. اگر آیتم یک فایل نباشد، این متد null را برمی‌گرداند. در حالی که می‌توانید فایل را بخوانید، هیچ راهی برای نوشتن مجدد در آن وجود ندارد. این متد این عیب را دارد که از دایرکتوری‌ها پشتیبانی نمی‌کند.

Browser Support

  • کروم: ۱۱.
  • لبه: ۱۲.
  • فایرفاکس: ۵۰.
  • سافاری: ۵.۱.

Source

بهبود تدریجی

قطعه کد زیر از متد DataTransferItem.getAsFileSystemHandle() در API دسترسی به سیستم فایل مدرن، در صورت پشتیبانی، استفاده می‌کند، سپس به متد غیراستاندارد DataTransferItem.webkitGetAsEntry() برمی‌گردد و در نهایت به متد کلاسیک DataTransferItem.getAsFile() برمی‌گردد. حتماً نوع هر handle را بررسی کنید، زیرا می‌تواند یکی از موارد زیر باشد:

  • FileSystemFileHandle وقتی مسیر کد مدرن انتخاب می‌شود.
  • وقتی مسیر کد کلاسیک انتخاب می‌شود، File .

همه نوع‌ها یک ویژگی name دارند، بنابراین ثبت آن مشکلی ندارد و همیشه کار خواهد کرد.

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

مطالعه بیشتر

نسخه آزمایشی

اچ‌تی‌ام‌ال

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