איך לטפל בקבצים שנפתחו מסייר הקבצים

Palances Liao
Palances Liao

הדרך המודרנית

שימוש ב-File Handling API

קודם כול, צריך להצהיר על מאפיין file_handlers בקובץ מניפסט של אפליקציה. ה-API לטיפול בקבצים דורש לציין את המאפיין action (כתובת URL לטיפול) ואת המאפיין accept, שהוא אובייקט עם סוגי MIME כמפתחות ומערכים של סיומות הקבצים התואמות.

{
  "file_handlers": [
    {
      "action": "./",
      "accept": {
        "image/*": [".jpg", ".jpeg", ".png", ".webp", ".svg"]
      }
    }
  ]
}

לאחר מכן, צריך להשתמש ב-File Handling API כדי לטפל באופן אימפרטיבי בקבצים שנפתחו באמצעות launchQueue.

if ('launchQueue' in window && 'files' in LaunchParams.prototype) {
  launchQueue.setConsumer((launchParams) => {
    if (!launchParams.files.length) {
      return;
    }
    for (const fileHandle of launchParams.files) {
      // Handle the file.
    }
  });
}

Browser Support

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

Source

הדרך הקלאסית

שימוש בשיטה הקלאסית DataTransferItem.getAsFile()

אם ממשק File Handling API לא נתמך, עדיין אפשר לגרור ולשחרר קבצים מסייר הקבצים אל האפליקציה. המתודה DataTransferItem.getAsFile() מחזירה את האובייקט File של פריט נתוני הגרירה. אם הפריט הוא לא קובץ, השיטה הזו מחזירה null. אתם יכולים לקרוא את הקובץ, אבל אין אפשרות לכתוב בו. החיסרון בשיטה הזו הוא שאי אפשר להשתמש בה בספריות.

Browser Support

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

Source

שיפור הדרגתי

קטע הקוד שבהמשך משתמש ב-File Handling API כשהוא זמין, ובנוסף רושם מטפלים בפעולות גרירה ושחרור כדי שיהיה אפשר לטפל בקבצים שנגררים.

מצהירים על סוגי הקבצים שאפשר לטפל בהם בקובץ מניפסט של אפליקציית האינטרנט. דפדפנים שלא תומכים ב-File Handling API פשוט יתעלמו מההגדרה הזו.

{
  "file_handlers": [
    {
      "action": "./",
      "accept": {
        "image/*": [".jpg", ".jpeg", ".png", ".webp", ".svg"]
      }
    }
  ]
}
// File Handling API
const handleLaunchFiles = () => {
  window.launchQueue.setConsumer((launchParams) => {
    if (!launchParams.files.length) {
      return;
    }
    launchParams.files.forEach(async (handle) => {
      const file = await handle.getFile();
      console.log(`File: ${file.name}`);
      // Do something with the file.
    });
  });
};

if ('launchQueue' in window && 'files' in LaunchParams.prototype) {
  handleLaunchFiles();
}

// 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) => 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.isFile) {
      console.log(`File: ${handle.name}`);
      // Do something with the file.
    }
  }
});

קריאה נוספת

הדגמה (דמו)

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="manifest" href="manifest.json" />
    <title>How to handle files opened from the file explorer</title>
    <link rel="stylesheet" href="style.css" />
    <!-- TODO: Devsite - Removed inline handlers -->
    <!-- <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', async () => {
          const registration = await navigator.serviceWorker.register(
            'sw.js',
          );
          console.log(
            'Service worker registered for scope',
            registration.scope,
          );
        });
      }
    </script>
    <script src="script.js" type="module"></script> -->
  </head>
  <body>
    <h1>How to handle files opened from the file explorer</h1>
    <p>Install the app. After the installation, try opening an image file from the file explorer with the app.
  </body>
</html>

CSS


        html {
  box-sizing: border-box;
  font-family: system-ui, sans-serif;
  color-scheme: dark light;
}

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

body {
  margin: 1rem;
}

img {
  height: auto;
  max-width: 100%;
  display: block;
}
        

JS


        if ('launchQueue' in window && 'files' in LaunchParams.prototype) {
  launchQueue.setConsumer((launchParams) => {
    if (!launchParams.files.length) {
      return;
    }
    for (const fileHandle of launchParams.files) {
      document.body.innerHTML += `

${fileHandle.name}

`; } }); }