【问题标题】:Chrome extension doing a download can't always specify file extension?下载的 Chrome 扩展不能总是指定文件扩展名?
【发布时间】:2020-07-23 12:03:40
【问题描述】:

我正在尝试制作一个 Chrome 扩展,通过 chrome.downloads.download 可以启动下载并指定生成的本地文件的文件名 - 特别是指定下载结果的文件扩展名。

如果服务器为此文件通告内容类型为 application/octet-stream,则可以正常工作。

但是,如果服务器发布了一些其他内容类型(例如 application/zip),则下载对象被设置为具有该 MIME 类型,并且下载的文件名被强制具有相关的扩展名(例如“. zip") 而不是我指定的那个。

我已尝试使用 onHeadersReceived 更改传入标头中的内容类型,将其强制为 application/octet-stream,但下载对象最终仍具有原始 MIME 类型并且文件扩展名仍是强制的。

使用 chrome.downloads.onDeterminingFilename 来“建议”我想要的文件名+扩展名也无助于防止强制扩展。我已经禁用了所有其他扩展,还检查了 onActionIgnored 是否会触发(它不会触发)。我终于为所有其他 webRequest 回调添加了日志记录,以查看是否有任何其他行为可以解释成功和失败案例之间的差异,但这个内容类型/mimetype 问题似乎是罪魁祸首。

这是在 Linux(基本操作系统,Ubuntu 变体)上加载到 Chrome 84.0.4147.89 中的解压缩扩展程序。我将在下面详细介绍扩展代码,但我认为可能只是关于如何创建下载对象的流程,要么 a) 使这样做变得不可能,要么 b) 意味着我需要这样做其他方式。

感谢您的帮助!

这是我的测试扩展的清单:

{
    "name": "Test DL Rename",
    "version": "1.0",
    "manifest_version": 2,
    "description": "Add a file extension to the name of a download.",
    "background": { 
      "scripts": ["background.js"],
      "persistent": true
    },
    "permissions": [
      "contextMenus",
      "downloads",
      "webRequest",
      "webRequestBlocking",
      "<all_urls>"
    ]
}

这是去掉所有调试繁琐的 background.js:

const CONTEXT_MENU_ID = "TEST_DL_RENAME";

// add our thing to the context menu for links
chrome.contextMenus.create(
  {
    id: CONTEXT_MENU_ID,
    title: "Test DL Rename",
    contexts: ["link"]
  }
);

// test download-renaming by adding ".qz" extension to downloaded file
chrome.contextMenus.onClicked.addListener(
  function(info, tab) {
    if (info.menuItemId !== CONTEXT_MENU_ID) {
      return;
    }
    filename = info.linkUrl.substring(info.linkUrl.lastIndexOf('/') + 1);
    qzFilename = filename + ".qz";
    console.log("specifying name for download: " + qzFilename);
    chrome.downloads.download(
      {
        url: info.linkUrl,
        filename: qzFilename,
        conflictAction: "uniquify"
      },
      function(downloadId) {
        console.log("started download ID " + downloadId);
      }
    );
  }
);

// test setting content-type on a received download
chrome.webRequest.onHeadersReceived.addListener(
  function(details) {
    // if doing this for real, we'd track which URLs we actually want to change
    // for now just change anything that is a zipfile
    if (details.url.split('.').pop() != "zip") {
      return {};
    }
    for (var i = 0; i < details.responseHeaders.length; i++) {
      if (details.responseHeaders[i].name.toLowerCase() == "content-type"){
        console.log("forcing content-type to application/octet-stream");
        details.responseHeaders[i].value = "application/octet-stream";
        break;
      }
    }
    return {
      responseHeaders: details.responseHeaders
    };
  },
  {
    urls: ["<all_urls>"]
  },
  ["blocking", "responseHeaders"]
);

这些是我目前用来运行测试的文件。如果我右键单击“使用 octet-stream MIME 类型进行测试”并选择“测试 DL 重命名”,则会根据需要生成名为“test.zip.qz”的下载。右键单击“test with zip MIME type”会得到“test-mime.zip.zip”而不是“test-mime.zip.qz”。

【问题讨论】:

    标签: google-chrome-extension


    【解决方案1】:

    看起来 Chrome 处理下载的一个有意限制是为了确保“安全”,你可以在 https://crbug.com 上通过宣传你的用例来提出异议。

    同时,自己下载 blob 并更改其类型:

    chrome.contextMenus.onClicked.addListener(async ({linkUrl: url}) => {
      const blob = await (await fetch(url)).blob();
      const typedBlob = blob.type === 'application/octet-stream' ? blob :
        new Blob([blob], {type: 'application/octet-stream'});
      chrome.downloads.download({
        url: URL.createObjectURL(typedBlob),
        filename: url.substring(url.lastIndexOf('/') + 1) + '.qz',
        conflictAction: 'uniquify',
      });
    });
    

    附:现在您不需要 webRequest API,您可以在 manifest.json 中使用 "persistent": false(FWIW 有一种方法可以同时使用两者,方法是将 webRequest 放入 optional_permissions,参见 documentation)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多