# 处理文件

> 分步指南：在 Web App Manifest 中声明 file_handlers，把已安装的 PWA 注册为系统级文件处理程序，接入 launchQueue，并在 API 不可用时回退。

import CompatTable from '@components/CompatTable.astro';

完成本指南后，桌面用户双击一个 `.csv` 文件，或在 "Open with" 里选中你的应用，就会进入已安装的 PWA 并且文件已经加载；Android、Firefox 或 Safari 上的用户则通过应用内文件选择器完成同样的导入。系统级关联来自 `file_handlers` manifest 成员，在 PWA 安装时读取；读取文件则在页面 JavaScript 中通过 `window.launchQueue` 完成。

你需要一个可安装的 PWA（见[入门](/zh/guides/getting-started/)）、Windows、macOS、Linux 或 ChromeOS 上的 Chrome 或 Edge 102 及以上，以及一种要接管的文件类型。

## 在 manifest 中声明处理程序

`file_handlers` 的每一项都需要一个位于应用导航作用域内的 `action` URL，以及一个从 MIME 类型映射到扩展名的 `accept` 对象（MDN 把两者都标为必填）。Chrome 还读取可选的 `icons` 数组（让系统显示文件类型图标而不是应用图标）和 `launch_type`：

```json
{
  "file_handlers": [
    {
      "action": "/open",
      "accept": { "text/csv": [".csv"] },
      "icons": [
        { "src": "/icons/csv-file.png", "sizes": "256x256", "type": "image/png" }
      ],
      "launch_type": "single-client"
    }
  ]
}
```

`launch_type` 默认为 `"single-client"`：同时打开多个匹配文件只产生一次启动，`LaunchParams.files` 包含全部文件。改为 `"multiple-clients"` 时，Chrome 为每个文件各启动应用一次，每次启动的 `files` 数组只有一个元素。修改该成员后请重新安装 PWA；系统关联在安装时写入，而且 Chrome 会在 `file_handlers` 变化时重置文件处理权限。

<CompatTable feature="manifest-file-handlers" />

Chrome 的能力文章写明文件处理 "limited to desktop operating systems"，所以即使 Android 接受这份 manifest，处理程序在那里也不起作用。

## 在页面中消费启动

当系统用该应用打开文件时，Chrome 导航到 `action` 并把一个 `LaunchParams` 对象放入 `window.launchQueue`；Chrome 的 launch-handler 文档把该队列描述为保留启动 "until they are handled by the specified consumer"，所以页面启动完成后才注册的 consumer 仍能收到启动。在顶层注册一次，不要放在任何异步启动逻辑之后：

```js
window.launchQueue.setConsumer(async (launchParams) => {
  if (!launchParams.files || launchParams.files.length === 0) return;
  for (const handle of launchParams.files) {
    const file = await handle.getFile();
    renderCsv(file.name, await file.text());
  }
});
```

`files` 中的每一项是文件句柄而不是 `File`，所以 consumer 在读取前要先调用 `getFile()`。

## 检测支持并回退到选择器

Chrome 的文章给出的检测是 `'launchQueue' in window && 'files' in LaunchParams.prototype`；后半句很重要，因为浏览器可能为 `launch_handler` 导航提供了 `launchQueue`，却没有 `files` 成员。检测失败时显示标准的文件输入框，让功能仍然存在，只是没有系统集成：

```js
function supportsFileHandling() {
  return 'launchQueue' in window && 'files' in LaunchParams.prototype;
}

const picker = document.querySelector('#open-file-input'); // <input type="file" accept=".csv">

if (supportsFileHandling()) {
  window.launchQueue.setConsumer(async (launchParams) => {
    for (const handle of launchParams.files ?? []) {
      const file = await handle.getFile();
      renderCsv(file.name, await file.text());
    }
  });
} else {
  picker.hidden = false;
  picker.addEventListener('change', async () => {
    const [file] = picker.files;
    if (file) renderCsv(file.name, await file.text());
  });
}
```

桌面上也要保留选择器入口：没有安装应用的用户，或先打开应用再想找文件的用户，都没有系统启动可依赖。

## 验证关联

安装 PWA，然后从系统文件管理器打开一个 `.csv`。首次启动时，应用读取文件前会出现 Chrome 的权限提示；允许后 consumer 运行，`files` 中有一项。

:::observed
Chrome 的文件处理文章（developer.chrome.com，2026-10-03 读取）记载，权限提示会在 "before a PWA can view a file" 时显示，并在每次启动时重复出现，直到用户选择 **Allow** 或 **Block**，或者 "ignores the prompt three times (after which Chromium will embargo and block this permission)"。因此测试期间三次关掉提示会悄悄禁用该来源的这项功能；应从站点权限设置里重置，而不是重装应用。
:::

## 另请参阅

- [`file_handlers` manifest 成员](/zh/reference/manifest/file-handlers/)
- [`file_handlers` 兼容性](/zh/compatibility/manifest-file-handlers/)
- [接收分享的内容（share target）](/zh/guides/share-target/)
- [File System Access](/zh/reference/capabilities/file-system-access/)
- [Let installed web applications be file handlers](https://developer.chrome.com/docs/capabilities/web-apis/file-handling)（developer.chrome.com）
- [`file_handlers`](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/file_handlers)（developer.mozilla.org）