让Chrome扩展程序下载文件

75

我正在创建一个扩展程序,用于从网站下载mp3文件。我试图通过创建一个包含mp3链接的新标签页来实现这一点,但Chrome总是在播放器内打开它,而不是下载它。有没有办法创建一个弹出窗口,让用户选择“另存为”文件?


2
请参考此链接:https://dev59.com/13RC5IYBdhLWcg3wJNcN - serg
5个回答

151

时光倒流3年,现在Google Chrome提供chrome.downloads API(自Chrome 31以来)。

在清单中声明"downloads"权限之后,可以使用此调用启动下载:

chrome.downloads.download({
  url: "http://your.url/to/download",
  filename: "suggested/filename/with/relative.path" // Optional
});

如果你想在脚本中生成文件内容,可以使用BlobURL API,例如:

var blob = new Blob(["array of", " parts of ", "text file"], {type: "text/plain"});
var url = URL.createObjectURL(blob);
chrome.downloads.download({
  url: url // The object URL can be used as download URL
  //...
});

查看文档以获取更多选项(例如“另存为”对话框、覆盖现有文件等)。


1
@marlar 为此,最好使用文件系统API(尽管有警告,但它在Chrome中受支持)来获取块,构建完整的文件,然后将结果下载到“下载”文件夹中。但这是一个不同的问题;如果您仍然需要帮助,请提出一个新问题。 - Xan
4
在我的系统上,“filename”被完全忽略了! - jumpjack
1
@Xan,我遇到了这个错误:Unchecked runtime.lastError while running downloads.download: Invalid filename,你有什么想法吗? - questionasker
4
@anunixercoder可能已经找到了问题,但是对于接下来的人:这段代码需要放在background.js上运行,不能放在content.js上。 - brasofilo
但是我可以请求使用POST方法来进行下载吗? - gumuruh
显示剩余6条评论

14

我使用了类似于这里的解决方案的变体

var downloadCSS = function () {
    window.URL = window.webkitURL || window.URL;
    file = new BlobBuilder(); //we used to need to check for 'WebKitBlobBuilder' here - but no need anymore
    file.append(someTextVar); //populate the file with whatever text it is that you want
    var a = document.createElement('a');
    a.href = window.URL.createObjectURL(file.getBlob('text/plain'));
    a.download = 'combined.css'; // set the file name
    a.style.display = 'none';
    document.body.appendChild(a);
    a.click(); //this is probably the key - simulatating a click on a download link
    delete a;// we don't need this anymore
}

你需要记住的一件事是,这段代码需要在页面上执行,而不是在你的扩展程序上执行——否则用户将看不到chrome进行的下载操作。下载仍然会发生,你可以在下载标签中看到它,但他们不会看到实际的下载过程。

编辑(关于使你的代码在内容页上执行的想法):

让动作在内容页上发生而不是在你的扩展程序上的方法是使用Chrome "消息传递"。基本上,你从扩展程序(几乎像一个单独的页面)向扩展程序正在处理的内容页传递一条消息。然后,你有一个监听器,你的扩展程序已经注入到内容页中,它会对消息做出反应并进行下载。就像这样:

chrome.extension.onMessage.addListener(
  function (request, sender, sendResponse) {  
      if (request.greeting == "hello") {
          try{
              downloadCSS();
          }
          catch (err) {
              alert("Error: "+err.message);
          }
      }
  });

在这里,“delete a;”并不是你想象中的那样,你实际上需要使用“a.parentNode.removeChild(a);”。(删除对a的引用并不是必要的,因为作为局部变量,它会在函数完成时丢失)。 - ejm
你知道这是否仍然有效吗?chrome.downloads不像我想要的那样工作。 - olfek
仍然可以工作,但是遇到了我在Chrome下载中遇到的同样问题。Chrome不允许扩展程序下载多个文件。 - olfek

12
这是对@Steve Mc答案的轻微修改,将其转换为通用函数,可以轻松地复制和使用如下:
function exportInputs() {
    downloadFileFromText('inputs.ini','dummy content!!')
}

function downloadFileFromText(filename, content) {
    var a = document.createElement('a');
    var blob = new Blob([ content ], {type : "text/plain;charset=UTF-8"});
    a.href = window.URL.createObjectURL(blob);
    a.download = filename;
    a.style.display = 'none';
    document.body.appendChild(a);
    a.click(); //this is probably the key - simulating a click on a download link
    delete a;// we don't need this anymore
}

1
对于现在的人来说:SteveMC的答案不再适用,但这个可以。 - Ian Hyzy

11

这里是使用Chrome扩展程序清单中的"downloads"权限下载文件的简明方法,使用@Xan和@AmanicA的解决方案

function downloadFile(options) {
    if(!options.url) {
        var blob = new Blob([ options.content ], {type : "text/plain;charset=UTF-8"});
        options.url = window.URL.createObjectURL(blob);
    }
    chrome.downloads.download({
        url: options.url,
        filename: options.filename
    })
}

// Download file with custom content
downloadFile({
  filename: "foo.txt",
  content: "bar"
});

// Download file from external host
downloadFile({
  filename: "foo.txt",
  url: "http://your.url/to/download"
});

非常感谢简洁明了,希望早点注意到它。没有必要模拟对一个隐藏的 a 标签的点击。谢谢! - SimplGy
这个脚本是在内容脚本(content script)、后台脚本(background script)还是其他地方执行的? 我在"chrome.downloads.download({"这一行代码上收到了一个“Uncaught TypeError: Cannot read property 'download' of undefined”的错误提示。 - Craig Lambie

6
Appmator代码的Github中,我按照以下步骤完成了它。基本方法是构建您想要的Blob(Chrome在XmlHttpRequest上有一个responseBlob,因此您可以使用它),创建一个iframe(hidden,display:none),然后将iframe的src分配为Blob。
这将启动下载并将其保存到文件系统中。唯一的问题是,您还不能设置文件名。
var bb = new (window.BlobBuilder || window.WebKitBlobBuilder)();

var output = Builder.output({"binary":true});
var ui8a = new Uint8Array(output.length);

for(var i = 0; i< output.length; i++) {
  ui8a[i] = output.charCodeAt(i);
}

bb.append(ui8a.buffer);

var blob = bb.getBlob("application/octet-stream");
var saveas = document.createElement("iframe");
saveas.style.display = "none";

if(!!window.createObjectURL == false) {
  saveas.src = window.webkitURL.createObjectURL(blob); 
}
else {
  saveas.src = window.createObjectURL(blob); 
}

document.body.appendChild(saveas);

使用XmlHttpRequest的responseBlob的示例(参见:http://www.w3.org/TR/XMLHttpRequest2/#dom-xmlhttprequest-responseblob

var xhr = new XmlHttpRequest();
xhr.overrideMimeType("application/octet-stream"); // Or what ever mimeType you want.
xhr.onreadystatechanged = function() {
if(xhr.readyState == 4 && xhr.status == 200) {

  var blob = xhr.responseBlob();
  var saveas = document.createElement("iframe");
  saveas.style.display = "none";

  if(!!window.createObjectURL == false) {
    saveas.src = window.webkitURL.createObjectURL(blob); 
  }
  else {
    saveas.src = window.createObjectURL(blob); 
  }

  document.body.appendChild(saveas);
}

我应该在哪里指定我要下载的文件? - Franz Payer
这取决于你。它可以是xhr请求中的responseBlob。 - Kinlan
你能否编辑代码,展示如何实现这个功能?我不知道在哪里放置位置。 - Franz Payer
你如何设置它以正确的文件类型下载?Chrome一直试图将其下载为html文件。创建blob的页面也会冻结并崩溃。似乎blob的来源也是空白的。 - Franz Payer
你能展示一些造成程序崩溃的代码示例吗?现在很难理解你是在做什么。我想说,我已经添加了一个小的代码片段来覆盖MIME类型。 - Kinlan
https://dev59.com/alfUa4cB1Zd3GeqPKK_L - Franz Payer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接