【问题标题】:Downloading an image using XMLHttpRequest in a userscript在用户脚本中使用 XMLHttpRequest 下载图像
【发布时间】:2012-02-05 10:10:09
【问题描述】:

首先,这里有一个question,标题相同,但这不是我要找的,也没有完整的答案。

所以这是我的问题。假设我有这个指向图片的 URL。

https://fbcdn-photos-a.akamaihd.net/hphotos-ak-ash4/299595_10150290138650735_543370734_8021370_355110168_n.jpg

一旦我将这个参数?dl=1 放在 URL 的末尾,它就可以下载了。

https://fbcdn-photos-a.akamaihd.net/hphotos-ak-ash4/299595_10150290138650735_543370734_8021370_355110168_n.jpg?dl=1

我正在尝试通过用户脚本来完成这项任务。所以我为此使用了 XMLHttpRequest。

var url = "https://fbcdn-photos-a.akamaihd.net/hphotos-ak-ash4/299595_10150290138650735_543370734_8021370_355110168_n.jpg?dl=1";

var request = new XMLHttpRequest();  
request.open("GET", url, false);   
request.send(null);  

if (request.status === 200) 
{  
    alert(request.statusText);
}

这是fiddle

但它不起作用。

【问题讨论】:

  • dl 论据在你的情况下完全是假的
  • @user539484 你是什么意思?它确实有效。只需将其复制并粘贴到地址栏上即可。
  • 我是认真的,看看它引起的响应头。此处不相关。

标签: javascript xmlhttprequest download userscripts


【解决方案1】:

XMLHttpRequest 不能跨域工作,但由于这是一个用户脚本,Chrome 现在仅支持用户脚本中的GM_xmlhttpRequest()

这样的东西应该可以工作,注意它是异步的:

GM_xmlhttpRequest ( {
    method:         'GET',
    url:            'https://fbcdn-photos-a.akamaihd.net/hphotos-ak-ash4/299595_10150290138650735_543370734_8021370_355110168_n.jpg?dl=1',
    onload:         function (responseDetails) {
                        alert(responseDetails.statusText);
                    }
} );




至于获取和使用实际图像数据,这是一个很大的难题。

  • 您可以在 Firefox 中使用新的 .responseType = "blob"; 功能,但 Chrome does not yet support it

  • 在 Chrome 或 Firefox 中,仅限同一域,您可以像这样使用新的XHR2
    See it in action at jsBin.

    BlobBuilder             = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
    
    var url                 = "http://jsbin.com/images/gear.png";
    var request             = new XMLHttpRequest();
    request.open ("GET", url, false);
    request.responseType    = "arraybuffer";
    request.send (null);
    
    if (request.status === 200) {
        var bb              = new BlobBuilder ();
        bb.append (request.response); // Note: not request.responseText
    
        var blob            = bb.getBlob ('image/png');
        var reader          = new FileReader ();
        reader.onload       = function (zFR_Event) {
            $("body").prepend ('<p>New image: <img src="' + zFR_Event.target.result + '"></p>')
        };
    
        reader.readAsDataURL (blob);
    }
    


  • 很遗憾,GM_xmlhttpRequest() 尚不支持设置 responseType


因此,对于 GM 脚本或用户脚本应用程序,我们必须使用自定义 base64 编码方案,如 "Javascript Hacks: Using XHR to load binary data"

脚本代码变成这样:

var imgUrl              = "http://jsbin.com/images/gear.png";

GM_xmlhttpRequest ( {
    method:         'GET',
    url:            imgUrl,
    onload:         function (respDetails) {
                        var binResp     = customBase64Encode (respDetails.responseText);

                        /*-- Here, we just demo that we have a valid base64 encoding
                            by inserting the image into the page.
                            We could just as easily AJAX-off the data instead.
                        */
                        var zImgPara    = document.createElement ('p');
                        var zTargetNode = document.querySelector ("body *"); //1st child

                        zImgPara.innerHTML = 'Image: <img src="data:image/png;base64,'
                                           + binResp + '">';
                        zTargetNode.parentNode.insertBefore (zImgPara, zTargetNode);
                    },
    overrideMimeType: 'text/plain; charset=x-user-defined'
} );


function customBase64Encode (inputStr) {
    var
        bbLen               = 3,
        enCharLen           = 4,
        inpLen              = inputStr.length,
        inx                 = 0,
        jnx,
        keyStr              = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
                            + "0123456789+/=",
        output              = "",
        paddingBytes        = 0;
    var
        bytebuffer          = new Array (bbLen),
        encodedCharIndexes  = new Array (enCharLen);

    while (inx < inpLen) {
        for (jnx = 0;  jnx < bbLen;  ++jnx) {
            /*--- Throw away high-order byte, as documented at:
              https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
            */
            if (inx < inpLen)
                bytebuffer[jnx] = inputStr.charCodeAt (inx++) & 0xff;
            else
                bytebuffer[jnx] = 0;
        }

        /*--- Get each encoded character, 6 bits at a time.
            index 0: first  6 bits
            index 1: second 6 bits
                        (2 least significant bits from inputStr byte 1
                         + 4 most significant bits from byte 2)
            index 2: third  6 bits
                        (4 least significant bits from inputStr byte 2
                         + 2 most significant bits from byte 3)
            index 3: forth  6 bits (6 least significant bits from inputStr byte 3)
        */
        encodedCharIndexes[0] = bytebuffer[0] >> 2;
        encodedCharIndexes[1] = ( (bytebuffer[0] & 0x3) << 4)   |  (bytebuffer[1] >> 4);
        encodedCharIndexes[2] = ( (bytebuffer[1] & 0x0f) << 2)  |  (bytebuffer[2] >> 6);
        encodedCharIndexes[3] = bytebuffer[2] & 0x3f;

        //--- Determine whether padding happened, and adjust accordingly.
        paddingBytes          = inx - (inpLen - 1);
        switch (paddingBytes) {
            case 1:
                // Set last character to padding char
                encodedCharIndexes[3] = 64;
                break;
            case 2:
                // Set last 2 characters to padding char
                encodedCharIndexes[3] = 64;
                encodedCharIndexes[2] = 64;
                break;
            default:
                break; // No padding - proceed
        }

        /*--- Now grab each appropriate character out of our keystring,
            based on our index array and append it to the output string.
        */
        for (jnx = 0;  jnx < enCharLen;  ++jnx)
            output += keyStr.charAt ( encodedCharIndexes[jnx] );
    }
    return output;
}

【讨论】:

  • 是的,它有效!谢谢你。 :) 小问题,您现在如何实际下载图像?我尝试了所有其他方法,但它们都返回信息、请求状态等。
  • 好吧.. 我读到了关于油脂点的overrideMimeType 论点,但它也没有提供太多细节。谢谢你:)
  • 好的,更新了显示如何抓取和 base64 编码数据的答案(这允许它在页面中使用或以可用形式 AJAXed 到服务器)。
  • 您可以将GM_ 函数放在其他函数中,但仅限于在脚本范围内运行的函数。如果包装函数被注入或以其他方式在页面范围内运行,则GM_ 函数将不起作用。这是设计使然(为了“安全”)。
  • 该行会阻止服务器响应被截断——否则在尝试将二进制数据视为文本时会发生这种情况——我们必须这样做,因为GM_xmlhttpRequest()(尚)不支持@987654337 @.
【解决方案2】:

您正在尝试使用 XHR 请求位于不同域中的资源,因此被阻止。使用 CORS 通过 XHR 进行跨域消息传递。

【讨论】:

    【解决方案3】:

    现代浏览器有Blob 对象:

    GM_xmlhttpRequest({
      method: "GET",
      url: url,
      headers: { referer: url, origin: url },
      responseType: 'blob',
      onload: function(resp) {
        var img = document.createElement('img');
        img.src = window.URL.createObjectURL(resp.response);
        document.body.appendChild(img);
      }
    });
    

    headers 参数将设置引荐来源网址,以便您可以加载引荐来源网址锁定的图像。

    【讨论】:

      【解决方案4】:

      Krof Drakula 是对的,您无法从其他域加载图像,但您真的需要这样做吗?您可以创建并附加一个 img 标记并等待它加载(使用 jQuery load() 之类的东西)。

      var img = document.createElement( 'img' );
      img.setAttribute( 'src', url );
      document.getElementsByTagName('body')[0].appendChild( img );
      

      【讨论】:

      • 不不...我不想在页面上嵌入图像。我想用 XHR 下载它。
      • 我不太明白你在做什么,但是如果你需要在页面上使用图像,你可以将它附加到隐藏的div中,例如,只是为了缓存它,并在加载后随心所欲地使用它。如果您需要显示保存对话框(请参阅上面的链接),则可以使用window.location = 'http://foo.com/bar?dl=1'。如果你真的需要使用 XHR,那么 CORS 可能是你唯一的选择。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-24
      • 1970-01-01
      • 2016-03-21
      • 2014-09-04
      相关资源
      最近更新 更多