4

My web application (HTML5 + JavaScript) needs to display PNG images that are generated by foreign web service.

However, that web service supports POST requests only. (More exactly, it does provide GET requests, but I have to transmit large arguments, due to which the GET URL becomes too long.)

Also, the web service has a different domain than the web application, and doesn't supply proper CORS headers, so Ajax (XMLHTTPRequest) doesn't work.

Is it still possible for my web application to load and display the foreign image via POST request?

I'm asking for a solution that is different from the following nasty workarounds, which are already well-known to me:

  • without setting up a local proxy that translates the request (and also circumvents the same-origin policy)
  • without using the remote proxy of some stranger
  • without using Flash
  • without using Java Applets
  • without using OS specific functionality such as ActiveX controls

However, a solution that fails to work with Internet Explorer is acceptible. Even a Firefox or Chrome specific solution is appreciated.

vog
  • 23,517
  • 11
  • 59
  • 75
  • Keep in mind, same origin policy is about not stealing cookies or personal data and is not about not accessing resources. If it was about not accessing resources proxies wouldn't work either. – Parris May 19 '15 at 15:58
  • does the service return a file or a url to an image? – Dave Alperovich May 19 '15 at 17:12
  • @DaveAlperovich: Not sure what you mean with "return a file", but the service returns the image data directly. It does not return a URL to another resource that returns the image. – vog May 24 '15 at 11:11
  • I think I would choose an ugly JSONP get over all the other hacks. Or at least I would try it first. Tough scenario. – Dave Alperovich May 24 '15 at 21:50
  • @DaveAlperovich: I have honestly no idea what you are talking about, or trying to say. – vog May 24 '15 at 22:24
  • Rather than a post, using a get with JSONP formatting. It means pushing all your data into url-encoded form, which is ugly. But using JSONP would solve the CORS issue without having to use an iFrame – Dave Alperovich May 24 '15 at 22:37
  • @DaveAlperovich: That still doesn't make any sense to be. To which service should the browser send the JSONP GET request? The original service doesn't know about JSONP. It simply expects a POST request and replies with an image, as stated in the question. – vog May 25 '15 at 12:23

3 Answers3

6

Horrible hack:

Submit a form to an iframe and have the image displayed in the iframe.

(But don't do this, it sounds like the web server is designed to avoid having images being embedded directly in other sites.)

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • No, the web service doesn't have problems with being inlined. It's just that the GET requests would be too large. I adjusted the question accordingly. – vog May 14 '15 at 10:04
  • without setting up a proxy I think this is the only viable solution – Hayko Koryun May 15 '15 at 08:59
  • This is awesome! Image sizing may be a problem if the image size is variable. – Parris May 19 '15 at 17:21
  • I'm very sorry. I intended to reward the bounty to this answer, but due to some irreversible mistake, it was awarded to the other answer. See also: https://meta.stackexchange.com/questions/257105/how-can-i-correct-a-bounty – vog May 25 '15 at 12:19
0

I have some possible solutions...

Solution 1

If your image is less that 25kb you can do the following via YQL: select * from data.uri where url="http://jquery.com/jquery-wp-content/themes/jquery/images/logo-jquery@2x.png" As a result you can just grab the base64 image and carry on. To do a POST via YQL you should add something like and postdata="foo=foo&bar=bar" check out this article.

Caveat: The performance of this method is probably not great. There's a fair amount of latency making the hop from the end user to YQL to the service and then going all the way back. Also there is some server side processing YQL does to base64 encode the image and deliver some JSON response.

Solution 2

Get CORS enabled or go through some other proxy. Once you do so, if you still can't get base64 data then you need to do 2 things. First add a jQuery transport that handles binary. Second process the binary blob and convert it to base64.

Here is a jQuery Binary Transport I found

$.ajaxTransport("+binary", function(options, originalOptions, jqXHR){
   // check for conditions and support for blob / arraybuffer response type
    if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob)))))
    {
        return {
            // create new XMLHttpRequest
            send: function(headers, callback){
        // setup all variables
                var xhr = new XMLHttpRequest(),
        url = options.url,
        type = options.type,
        async = options.async || true,
        // blob or arraybuffer. Default is blob
        dataType = options.responseType || "blob",
        data = options.data || null,
        username = options.username || null,
        password = options.password || null;

                xhr.addEventListener('load', function(){
            var data = {};
            data[options.dataType] = xhr.response;
            // make callback and send data
            callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
                });

                xhr.open(type, url, async, username, password);

        // setup custom headers
        for (var i in headers ) {
            xhr.setRequestHeader(i, headers[i] );
        }

                xhr.responseType = dataType;
                xhr.send(data);
            },
            abort: function(){
                jqXHR.abort();
            }
        };
    }
});

Once you add the transport you can make any sort of AJAX request.

$.ajax({
    type: "POST",
    url: 'http://myservice.com/service/v1/somethingsomething',
    dataType: 'binary',
    success: function(imgData) {
        var img = new Image(),
            reader = new window.FileReader();

        reader.readAsDataURL(imgData); 
        reader.onloadend = function() {
            img.src = reader.result
            $('#logo-events').append(img);
        }
    }
});

The reader should take the Blob and output a base64 version. When the reader is done converting/reading it will create and image and append it somewhere. GET or POST should not matter any more.

Parris
  • 17,833
  • 17
  • 90
  • 133
  • Solution 1 proposes to use some Yahoo servers as a proxy. I believe this is an even more horrible act than setting up a local proxy. I adjusted my question to make this point more clear. – vog May 19 '15 at 08:52
  • 1
    -1 for "Solution 2", because it totally misses the point. I have no influence of the CORS headers, which is clearly stated in the question. And "through some other proxy" would mean setting up a proxy, which is the exact solution I'm aware of and to which I'm asking for a better solution. (Also, in that case I would simply use an IMG tag and wouldn't bother with AJAX, so this solution is over-engineered as well.) – vog May 19 '15 at 09:06
  • @vog just because the answer is no doesn't mean it deserves -1. The only way to do a POST is via an AJAX call. Also that is the purpose of yql, it is meant to serve as a proxy. You are asking how to get around same origin policy #1 says how #2 says don't but if you still need POST this is how. – Parris May 19 '15 at 15:48
  • 1
    The whole point of the question (and the bounty) is because it is hard. And the answer "no" is not correct, see the iframe trick by Quentin. I'm looking for more creative stuff like that. I'm not interested in answers that try to sell me what I already know. – vog May 19 '15 at 19:15
0

I found this related question: Post data to JsonP

And I think that it could be applicable in your case.

Basically, fire your jsonp request to your server (same-origin-policy should not be a problem), and load the response an <img>

Like @Quentin's answer, this hack uses a (hidden) Iframe

Community
  • 1
  • 1
Uri Goren
  • 13,386
  • 6
  • 58
  • 110