用chrome插件下载文件,并自定义文件名 - 小众知识

用chrome插件下载文件,并自定义文件名

2013年01月27日 14:18:05 苏内容
  标签: chrome/下载
阅读:279

突然接到一个需求,是用chrome extensions下载一批文件,但文件名需要是url中的id名字。

要修改下载过来的文件名?这需求挺奇葩,但做为开发,一定要想办法把完成这样的下需要。

第一步,看chrome插件开发流程,一个manifest文件指定一个html文件,就可以完成hello world的制作。

一般的插件都会把脚本放到宿主页面上,通过宿主上去的脚本和插件里的脚本进行消息交互,如此可以把宿主页面上的dom节点和相关信息通过消息传递传递到插件脚本。

需求方给了我一个download master的插件,他的功能都是完整的,只是下载过来的文件名是服务器里用content-disposition指定了文件名的文件。

想了四个办法来完成修改文件名。

1.如果下载的文件是网页上的静态文件,如图片,html,css等资源文件,可以生成一个a标签,把href赋值成资源的url,同时给a标签的download属性赋值要下载后的文件名, 再用一个鼠标模拟点击的方法,自动下载上面的这些资源,下面的这段脚本是放到宿主上去的脚本里,通过消息传递触发

  1. function downloadResource(url){
  2. var link = document.createElement('a');
  3. a.href= url;
  4. a.download = "xxxxx.xxxx";
  5. var event = document.createEvent("MouseEvents");
  6. event.initMouseEvent("click", true, false);
  7. link.dispatchEvent(event);
  8. }


2.用chrome插件的chrome.downloads.download方法,这个方法是要用chrome的开发版本,于是马上安装开发版,并在flag里把实验扩展接口开启,用到上面的这个方法是看到api说明里有filename的项可供修改,事实证明,这个filename项和效果的第一种方法的效果是一样的,对于服务器指定了content-disposition的header属性的文件不起作用。不起作用的方法也不多说了,都是无用功。

chrome.downloads.download({url: torrentsLinks[i],filename:'1.torrent'},function(id) {});

3.通过上面的几种尝试,已经知道服务器指定了的content-disposition让我改不了文件名,想办法在responseHeader里把这个属性改掉,终于在chrome插件api里找到了可以修改的方法chrome.webRequest.onHeadersReceived,这个对象可以在服务器的header接收完成触发,突破口已经找到了,可这个方法怎么写呢,先看一源码

  1. chrome.webRequest.onHeadersReceived.addListener(function(details){
  2. var headers = details.responseHeaders,blockingResponse = {};
  3. for( var i = 0, l = headers.length; i < l; ++i ) {
  4. if( (headers[i].name.toLowerCase() == 'content-disposition') ) {
  5. headers[i].value = 'attachment; filename=\"xxxx.xxxx\"';
  6. break;
  7. }
  8. }
  9. blockingResponse.responseHeaders = headers;
  10. return {
  11. responseHeaders: headers
  12. }
  13. },{urls: ["http://jpopsuki.eu/*","https://what.cd/*"]},["responseHeaders","blocking"]);


addListener有三个参数,第一个是回调函数,第二个是过滤的urls,就是只有这些url才进行监听,第三个参数是一定要有blocking字符串,要是没有这个字符串,回调函数只要调用一下,不等回调函数返回已经把responseHeader拿去下载了,而第三个参数responseHeaders是只对responseHeaders进行监听,如此完成了一个下载文件名的修改需求。


上面的的开发过程中找到一个chromeHidden.internalApis.downloads.onDeterminingFilename从api的说明上来看是可以支持文件名的修改的,可是在实际调用过程中抛出了错误,找不到internalApis对象,在chrome上记了一个bug,不知道会不会引起chrome的注意。




  在扩展内使用 Ajax 可引入 jQuery 库。

        下载使用 chrome.downloads API,此 API 只能在 background 中使用,content_scripts 中使用会报错。所以在 content_scripts 中获取到要下载的资源后,通过消息(chrome.runtime.sendMessage() API)发送给 background 进行下载。


使用 downloads API 需要在 manifest 文件配置 permissions :

"permissions": [	"downloads",	"storage"],


这段是从另外的应用里摘出来的

  1. /*
  2. 0 - (未初始化)还没有调用send()方法
  3. 1 - (载入)已调用send()方法,正在发送请求
  4. 2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
  5. 3 - (交互)正在解析响应内容
  6. 4 - (完成)响应内容解析完成,可以在客户端调用了
  7. @see https://xhr.spec.whatwg.org/
  8. */
  9. function ajaxRequest(curhost, rurl, isFinished){
  10. var xhr = new XMLHttpRequest();
  11. xhr.onreadystatechange = function (){
  12. if(xhr.readyState == 1) {
  13. //alert( '请稍等正在刷新...' );
  14. }
  15. if (xhr.readyState == 2) {
  16. if(isFinished == 'ok'){
  17. //通知重载页面
  18. flushFinished();
  19. return '';
  20. }else{
  21. ajaxRequest(curhost, rurl, 'ok');
  22. }
  23. }
  24. };
  25. xhr.open("GET",curhost + rurl + id, true);
  26. xhr.send();
  27. }


下载调用的 API :

  1. chrome.downloads.download({
  2. url: url,
  3. filename: filename,
  4. conflictAction: 'uniquify',
  5. saveAs: false
  6. },
  7. function(res){
  8. });


自己封装后,配合选项页使用:

  1. //保存文件方式
  2. var _ex_save_conflict = 'overwrite';
  3. chrome.storage.local.get('_ex_favpicture_likename', function(valueArray) {
  4. if( valueArray._ex_favpicture_likename ){
  5. if( valueArray._ex_favpicture_likename == 'prompt' || valueArray._ex_favpicture_likename == 'uniquify' ){
  6. _ex_save_conflict = valueArray._ex_favpicture_likename;
  7. }
  8. }
  9. });
  10. //保存图片
  11. /*
  12. {
  13.    url: 下载文件的url,
  14.    filename: 保存的文件名,
  15.    conflictAction: 重名文件的处理方式,uniquify 括号防重,overwrite 覆盖,prompt 弹窗让用户决定
  16.    saveAs: 是否弹出另存为窗口,
  17.    method: 请求方式(POST或GET),
  18.    headers: 自定义header数组,
  19.    body: POST的数据
  20. }
  21. */
  22. function _exSavePicture(url, filename){
  23. chrome.downloads.download({
  24. url: url,
  25. filename: filename,
  26. conflictAction: _ex_save_conflict,
  27. saveAs: false
  28. }, function(res){  });
  29. }


每次调用下载是单文件的,所以一组数据/对象 要循环调用。


扩展阅读