与其他应用共享内容

在应用之间共享内容在移动设备上很流行,在这些设备上,操作文件或复制内容不如桌面操作系统直观。 例如,在移动设备上,通常通过发送短信与朋友共享图像。 但共享内容不保留给移动设备:还可以在 Windows 上的应用之间共享。

共享内容有两个方向,渐进式 Web 应用 (PWA) 可以处理这两个方向:

方向 说明
共享内容 若要共享内容,PWA 会) 生成内容 ((如文本、链接或文件),并将共享内容移交给操作系统。 操作系统允许用户决定要使用哪个应用来接收该内容。
接收共享内容 若要接收内容,PWA 充当内容目标。 PWA 作为内容共享目标注册到操作系统。

注册为共享目标的 PWA 感觉本机集成到 OS 中,并且对用户更具吸引力。

共享内容

PWA 可以使用 Web 共享 API 触发显示操作系统共享对话框。

注意

Web 共享仅适用于通过 HTTPS 服务的网站, (这是 PWA) 的情况,并且只能在响应用户操作时调用。

若要共享链接、文本或文件等内容,请使用 navigator.share 函数,如下所示。 函数 navigator.share 接受应至少具有以下属性之一的对象:

  • title:共享内容的简短标题。
  • text:共享内容的较长说明。
  • url:要共享的资源的地址。
  • files:要共享的文件数组。
function shareSomeContent(title, text, url) {
  if (!navigator.share) {
    return;
  }

  navigator.share({title, text, url}).then(() => {
    console.log('The content was shared successfully');
  }).catch(error => {
    console.error('Error sharing the content', error);
  });
}

在上面的代码中,我们首先通过测试 是否 navigator.share 定义了 来检查浏览器是否支持 Web 共享。 该 navigator.share 函数返回一个 Promise 对象,该对象在共享成功时解析,并在发生错误时拒绝。

由于此处使用了 Promise,因此可将上述代码重写为函数 async ,如下所示:

async function shareSomeContent(title, text, url) {
  if (!navigator.share) {
    return;
  }

  try {
    await navigator.share({title, text, url});
    console.log('The content was shared successfully');
  } catch (e) {
    console.error('Error sharing the content', e);
  }
}

在 Windows 上,上述代码将触发共享对话框,允许用户选择应用来接收共享内容。 共享对话框如下所示:

Windows 上的共享对话框

用户选择应用接收共享内容后,由此应用以任何方式处理它。 例如,电子邮件应用可能会使用 title 作为电子邮件主题,并使用 text 作为电子邮件正文。

共享文件

函数 navigator.share 还接受数组 files 以与其他应用共享文件。

在共享文件之前,请务必测试浏览器是否支持共享文件。 若要检查是否支持共享文件,请使用 navigator.canShare 函数:

function shareSomeFiles(files) {
  if (navigator.canShare && navigator.canShare({files})) {
    console.log('Sharing files is supported');
  } else {
    console.error('Sharing files is not supported');
  }
}

共享 files 对象成员必须是对象的数组 File 。 详细了解 文件接口

构造 File 对象的一种方法是:

  1. 首先,使用 fetch API 请求资源。
  2. 然后,使用返回的响应创建新的 File

此方法如下所示。

async function getImageFileFromURL(imageURL, title) {
  const response = await fetch(imageURL);
  const blob = await response.blob();
  return new File([blob], title, {type: blob.type});
}

在上面的代码中:

  1. 函数 getImageFileFromURL 使用 URL 提取图像。
  2. 函数 response.blob() 将图像转换为 BLOB) (二进制大型对象。
  3. 代码使用 BLOB 创建 File 对象。

共享内容的演示

PWAmp 是一个演示 PWA,它使用 navigator.share 函数共享文本和链接。

若要测试“共享”功能,请执行以下操作:

  1. 转到 PWAmp

  2. 在“地址”栏的右侧,单击“ 可用应用”。安装 (PWA“应用可用,安装”图标) 按钮将 PWAmp 安装为 PWA。

  3. 在已安装的 PWAmp PWA 中,将本地音频文件 (拖动到应用窗口) 。 例如,如果克隆了 MicrosoftEdge/Demos 存储库,则 (Demos 存储库 > pwamp/songs 目录) 具有文件的本地副本.mp3,例如 C:\Users\username\GitHub\Demos\pwamp\songs

  4. 在新导入的歌曲旁边,单击“ 歌曲操作 (...) ”按钮,然后选择“ 共享”。 将显示“Windows 共享 ”对话框:

    “Windows 共享”对话框,用于选择哪个应用接收共享内容

  5. 选择要在其中共享内容的应用。

可以在 GitHub 上找到 PWAmp 源代码 。 PWAmp 应用使用 app.js 源文件中的 Web 共享 API。

接收共享内容

通过使用 Web 共享目标 API,PWA 可以注册为在系统共享对话框中显示为应用。 然后,PWA 可以使用 Web 共享目标 API 来处理来自其他应用的共享内容。

注意

只有已安装的 PWA 才能注册为共享目标。

注册为目标

若要接收共享内容,首先要做的是将 PWA 注册为共享目标。 若要注册,请使用 share_target 清单成员。 安装应用后,操作系统将使用 成员 share_target 将应用包含在系统共享对话框中。 操作系统知道在用户选取应用时要执行哪些操作来共享内容。

成员 share_target 必须包含系统向应用传递共享内容所需的信息。 请考虑以下清单代码:

{
    "share_target": {
        "action": "/handle-shared-content/",
        "method": "GET",
        "params": {
            "title": "title",
            "text": "text",
            "url": "url",
        }
    }
}

当用户选择应用作为共享内容的目标时,将启动 PWA。 对 GET 属性指定的 action URL 发出 HTTP 请求。 共享数据作为 titletexturl 查询参数传递。 发出以下请求: /handle-shared-content/?title=shared title&text=shared text&url=shared url

如果现有代码使用其他查询参数名称,则可以将默认 titletexturl 查询参数映射到其他名称。 在以下示例中 title, 、 texturl 查询参数映射到 subjectbodyaddress

{
    "share_target": {
        "action": "/handle-shared-content/",
        "method": "GET",
        "params": {
            "title": "subject",
            "text": "body",
            "url": "address",
        }
    }
}

处理 GET 共享数据

若要处理 PWA 代码中通过 GET 请求共享的数据,请使用 URL 构造函数提取查询参数:

window.addEventListener('DOMContentLoaded', () => {
    console url = new URL(window.location);

    const sharedTitle = url.searchParams.get('title');
    const sharedText = url.searchParams.get('text');
    const sharedUrl = url.searchParams.get('url');
});

处理 POST 共享数据

如果共享数据旨在以任何方式更改应用(例如通过更新存储在应用中的某些内容),则必须使用 POST 方法并使用 定义编码类型 enctype

{
    "share_target": {
        "action": "/post-shared-content",
        "method": "POST",
        "enctype": "multipart/form-data",
        "params": {
            "title": "title",
            "text": "text",
            "url": "url",
        }
    }
}

POST HTTP 请求包含编码为 的multipart/form-data共享数据。 可以使用服务器端代码访问 HTTP 服务器上的此数据,但当用户脱机时,此方法不起作用。 若要提供更好的体验,请使用服务辅助角色,并使用 fetch 事件侦听器访问服务辅助角色中的数据,如下所示:

self.addEventListener('fetch', event => {
    const url = new URL(event.request.url);

    if (event.request.method === 'POST' && url.pathname === '/post-shared-content') {
        event.respondWith((async () => {
            const data = await event.request.formData();

            const title = data.get('title');
            const text = data.get('text');
            const url = data.get('url');

            // Do something with the shared data here.

            return Response.redirect('/content-shared-success', 303);
        })());
    }
});

在上面的代码中:

  1. 服务辅助角色截获请求 POST

  2. 以某种方式使用数据 ((例如)将内容存储在本地) 。

  3. 将用户重定向到成功页面。 这样,即使网络关闭,应用也能正常工作。 应用可以选择仅在本地存储内容,也可以在以后 (使用 后台同步) 等方式还原连接时将内容发送到服务器。

处理共享文件

应用还可以处理共享文件。 若要处理 PWA 中的文件,必须使用 POST 方法和 multipart/form-data 编码类型。 此外,必须声明应用可以处理的文件类型。

{
    "share_target": {
        "action": "/store-code-snippet",
        "method": "POST",
        "enctype": "multipart/form-data",
        "params": {
            "title": "title",
            "files": [
                {
                    "name": "textFile",
                    "accept": ["text/plain", "text/html", "text/css", 
                               "text/javascript"]
                }
            ]
        }
    }
}

上述清单代码告知系统,你的应用可以接受具有各种 MIME 类型的文本文件。 文件扩展名(如 .txt)也可在数组中 accept 传递。

若要访问共享文件,请使用 formData 之前的请求,并使用 FileReader 来读取内容,如下所示:

self.addEventListener('fetch', event => {
    const url = new URL(event.request.url);

    if (event.request.method === 'POST' && url.pathname === '/store-code-snippet') {
        event.respondWith((async () => {
            const data = await event.request.formData();

            const filename = data.get('title');
            const file = data.get('textFile');

            const reader = new FileReader();
            reader.onload = function(e) {
                const textContent = e.target.result;

                // Do something with the textContent here.

            };
            reader.readAsText(file);

            return Response.redirect('/snippet-stored-success', 303);
        })());
    }
});

另请参阅