Cách xử lý các tệp được mở qua trình khám phá tệp

Palances Liao
Palances Liao

Cách làm hiện đại

Sử dụng File Handling API

Trước tiên, hãy khai báo thuộc tính file_handlers trong tệp kê khai ứng dụng web. File Handling API yêu cầu bạn chỉ định thuộc tính action (URL xử lý) và thuộc tính accept. Đây là một đối tượng có các loại MIME làm khoá và mảng của các tiện ích tệp tương ứng cụ thể.

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

Tiếp theo, bạn cần sử dụng API Xử lý tệp để xử lý bắt buộc các tệp đã mở thông qua 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

Cách cổ điển

Sử dụng phương thức DataTransferItem.getAsFile()

Nếu File Handling API không được hỗ trợ, bạn vẫn có thể kéo và thả tệp từ trình khám phá tệp vào ứng dụng. Phương thức DataTransferItem.getAsFile() sẽ trả về đối tượng File của mục dữ liệu kéo. Nếu mục không phải là tệp, phương thức này sẽ trả về null. Mặc dù bạn có thể đọc tệp, nhưng không có cách nào để ghi lại vào tệp đó. Phương thức này có nhược điểm là không hỗ trợ thư mục.

Browser Support

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

Source

Cải tiến tăng dần

Đoạn mã bên dưới sử dụng File Handling API khi có sẵn, đồng thời đăng ký các trình xử lý kéo và thả để có thể xử lý các tệp được kéo.

Khai báo các loại tệp có thể được xử lý trong tệp kê khai ứng dụng web. Những trình duyệt không hỗ trợ File Handling API sẽ bỏ qua yêu cầu này.

{
  "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.
    }
  }
});

Tài liệu đọc thêm

Bản minh hoạ

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}

`; } }); }