在 ManifestV3 Service Worker 中将大型 Blob 下载到本地文件

问题描述 投票:0回答:1

我有一个日志记录机制,可以将日志保存到数组中。我需要一种将日志下载到文件中的方法。

我之前(在清单 v2 上)使用过此功能

const url = URL.createObjectURL(new Blob(reallyLongString, { type: 'text/plain' }));
const filename = 'logs.txt';
chrome.downloads.download({url, filename});

现在我正在迁移到清单 v3,并且由于清单 v3 没有

URL.createObjectURL
,因此您无法创建要传递到
chrome.downloads.download

的 url

相反,可以使用类似的东西创建 Blob URL

const url = `data:text/plain,${reallyLongString}`; 
const filename = 'logs.txt';
chrome.downloads.download({url, filename});

问题

chrome.downloads.download
似乎对url参数中传递的字符数有限制,并且下载的文件只包含字符串的一小部分。

那么有什么方法可以克服这个限制呢?

google-chrome-extension chrome-extension-manifest-v3
1个回答
1
投票

希望在 https://crbug.com/1224027 中实现直接在 Service Worker 中下载 Blob 的方法。

通过屏幕外文档解决方法

示例:https://stackoverflow.com/a/77427098

它使用 chrome.offscreen API 启动一个不可见的 DOM 页面,我们可以在其中调用 URL.createObjectURL,将结果传递回 SW,SW 将使用它进行 chrome.downloads.download。

通过扩展页面解决方法

算法如下:

  1. 使用已打开的页面,例如弹出窗口或选项
  2. 否则,将 iframe 注入到我们有权访问的任何页面中
  3. 否则,打开一个新的最小化窗口
async function downloadBlob(blob, name, destroyBlob = true) {
  // When `destroyBlob` parameter is true, the blob is transferred instantly,
  // but it's unusable in SW afterwards, which is fine as we made it only to download
  const send = async (dst, close) => {
    dst.postMessage({blob, name, close}, destroyBlob ? [await blob.arrayBuffer()] : []);
  };
  // try an existing page/frame
  const [client] = await self.clients.matchAll({type: 'window'});
  if (client) return send(client);
  const WAR = chrome.runtime.getManifest().web_accessible_resources;
  const tab = WAR?.some(r => r.resources?.includes('downloader.html'))
    && (await chrome.tabs.query({url: '*://*/*'})).find(t => t.url);
  if (tab) {
    chrome.scripting.executeScript({
      target: {tabId: tab.id},
      func: () => {
        const iframe = document.createElement('iframe');
        iframe.src = chrome.runtime.getURL('downloader.html');
        iframe.style.cssText = 'display:none!important';
        document.body.appendChild(iframe);
      }
    });
  } else {
    chrome.windows.create({url: 'downloader.html', state: 'minimized'});
  }
  self.addEventListener('message', function onMsg(e) {
    if (e.data === 'sendBlob') {
      self.removeEventListener('message', onMsg);
      send(e.source, !tab);
    }
  });
}

下载器.html:

<script src=downloader.js></script>

downloader.js、popup.js、options.js 和其他扩展页面脚本(不是内容脚本):

navigator.serviceWorker.ready.then(swr => swr.active.postMessage('sendBlob'));
navigator.serviceWorker.onmessage = async e => {
  if (e.data.blob) {
    await chrome.downloads.download({
      url: URL.createObjectURL(e.data.blob),
      filename: e.data.name,
    });
  }
  if (e.data.close) {
    window.close();
  }
}

manifest.json:

"web_accessible_resources": [{
  "matches": ["<all_urls>"],
  "resources": ["downloader.html"],
  "use_dynamic_url": true
}]

警告! 由于

"use_dynamic_url": true
尚未实现,如果您不想让网页检测到您的扩展,请不要添加 web_accessible_resources。

© www.soinside.com 2019 - 2024. All rights reserved.