Downloading an image using XMLHttpRequest in a userscript

烂漫一生 提交于 2019-11-27 19:50:38

XMLHttpRequest will not work cross-domain, but since this is a userscript Chrome now supports GM_xmlhttpRequest() in userscripts only.

Something like this should work, note that it is asynchronous:

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);
                    }
} );




As for getting and using the actual image data, that is a major pain to work out.

  • You can use the new .responseType = "blob"; functionality in Firefox but Chrome does not yet support it.

  • In Chrome or Firefox, for the same domain only, you can use the new XHR2 like so:
    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);
    }
    


  • Unfortunately, GM_xmlhttpRequest() does not (yet) support setting responseType.


So, for GM script or userscript applications, we have to use a custom base64 encoding scheme like in "Javascript Hacks: Using XHR to load binary data".

The script code becomes something like:

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;
}

You are trying to request a resource using XHR that is on a different domain and is thus blocked. Use CORS for cross-domain messaging using XHR.

Krof Drakula is right, you cannot load an image from a different domain, but do you really need to do this? You can create and append an img tag and wait for it to load (with something like jQuery load()).

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

Modern browsers have the Blob object:

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);
  }
});

The headers param will set the referrer so you can load referrer locked images.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!