100

I recently asked another (related) question, which lead to this follow up question: Submitting data instead of a file for an input form

Reading through the jQuery.ajax() documentation (http://api.jquery.com/jQuery.ajax/), it seems the list of accepted dataTypes doesn't include images.

I'm trying to retrieve an image using jQuery.get (or jQuery.ajax if I have to), store this image in a Blob and upload it to another server in a POST request. Currently, it looks like due to the mismatch in datatypes, my images end up being corrupt (size in bytes mismatch, etc.).

The code to perform this is as follows (it is in coffeescript but shouldn't be difficult to parse):

handler = (data,status) ->
  fd = new FormData
  fd.append("file", new Blob([data], { "type" : "image/png" }))
  jQuery.ajax {
    url: target_url,
    data: fd,
    processData: false,
    contentType: "multipart/form-data",
    type: "POST",
    complete: (xhr,status) ->
      console.log xhr.status
      console.log xhr.statusCode
      console.log xhr.responseText

  }
jQuery.get(image_source_url, null, handler)

How can I retrieve this image as a blob instead?

Community
  • 1
  • 1
jabalsad
  • 2,351
  • 6
  • 23
  • 28
  • I think you have to change the response type in the server side. – Eric Frick Jul 15 '13 at 14:52
  • I'm trying to pull an image from any url, not a server I own necessarily. – jabalsad Jul 15 '13 at 14:59
  • take a look of this http://stackoverflow.com/questions/4285042/can-jquery-ajax-load-image – Eric Frick Jul 15 '13 at 15:03
  • It seems like the three solutions in that answer is to either (1) use the tag, (2) make the server serve the images as byte64 encoded or (3) use the browser's cache. (2) is ruled out because I want the script to work any image url. I'm not sure how to use (1) or (3) because once I have downloaded the image I need to convert it to a Blob. – jabalsad Jul 15 '13 at 15:14
  • option 3 would only work if you've already downloaded the image. For the very first time you will need something different. Maybe opt 1? – Eric Frick Jul 15 '13 at 15:20
  • you can use plain JS at any point inside a jQuery application: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data – dandavis Jul 15 '13 at 15:29

3 Answers3

182

You can't do this with jQuery ajax, but with native XMLHttpRequest.

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
    if (this.readyState == 4 && this.status == 200){
        //this.response is what you're looking for
        handler(this.response);
        console.log(this.response, typeof this.response);
        var img = document.getElementById('img');
        var url = window.URL || window.webkitURL;
        img.src = url.createObjectURL(this.response);
    }
}
xhr.open('GET', 'http://jsfiddle.net/img/logo.png');
xhr.responseType = 'blob';
xhr.send();      

EDIT

So revisiting this topic, it seems it is indeed possible to do this with jQuery 3

jQuery.ajax({
        url:'https://images.unsplash.com/photo-1465101108990-e5eac17cf76d?ixlib=rb-0.3.5&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ%3D%3D&s=471ae675a6140db97fea32b55781479e',
        cache:false,
        xhr:function(){// Seems like the only way to get access to the xhr object
            var xhr = new XMLHttpRequest();
            xhr.responseType= 'blob'
            return xhr;
        },
        success: function(data){
            var img = document.getElementById('img');
            var url = window.URL || window.webkitURL;
            img.src = url.createObjectURL(data);
        },
        error:function(){
            
        }
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<img id="img" width=100%>

or

use xhrFields to set the responseType

    jQuery.ajax({
            url:'https://images.unsplash.com/photo-1465101108990-e5eac17cf76d?ixlib=rb-0.3.5&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ%3D%3D&s=471ae675a6140db97fea32b55781479e',
            cache:false,
            xhrFields:{
                responseType: 'blob'
            },
            success: function(data){
                var img = document.getElementById('img');
                var url = window.URL || window.webkitURL;
                img.src = url.createObjectURL(data);
            },
            error:function(){
                
            }
        });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
    <img id="img" width=100%>
Musa
  • 96,336
  • 17
  • 118
  • 137
  • Thanks. I just figured it out and saw your answer. It's similar to mine (other than the fact I post it to a form instead of setting it on a object). I'll mark it as correct anyway :) – jabalsad Jul 17 '13 at 16:22
  • @jabalsad the question asked `How can I retrieve this image as a blob instead?`, anyway that was just to demo it, `handler` will take `this.response` and add it to a formdata object and send it via ajax. – Musa Jul 17 '13 at 16:23
  • 2
    +1, works great! And FYI, this may finally get added to jQuery soon: https://github.com/jquery/jquery/pull/1525 – lambshaanxy Mar 04 '14 at 01:39
  • @jpatokal not anymore – Musa May 07 '15 at 18:59
  • 11
    2017: jQuery is still not able to deal with type 'blob'? – robsch Jan 23 '17 at 12:53
  • @TrevorD What version of jQuery are you using? – Musa Sep 20 '18 at 18:46
  • Ah that explains it, version 1.12.4 for jquerymobile compatibility. I'll delete my comment and hang my head in shame – Trevor Sep 21 '18 at 15:40
  • 1
    'xhrFields' also works in jQuery 2 (tested in 2.2.4) – Bampfer May 22 '19 at 15:09
  • Good detailed answer for both vanilla javascript coders and normal jquery based coders :D – vibs2006 Nov 20 '19 at 13:51
  • oh man! thanks you very much! This xhr:function() did it! Few hours of searching :D – Chytry Mar 22 '22 at 17:57
34

If you need to handle error messages using jQuery.AJAX you will need to modify the xhr function so the responseType is not being modified when an error happens.

So you will have to modify the responseType to "blob" only if it is a successful call:

$.ajax({
    ...
    xhr: function() {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 2) {
                if (xhr.status == 200) {
                    xhr.responseType = "blob";
                } else {
                    xhr.responseType = "text";
                }
            }
        };
        return xhr;
    },
    ...
    error: function(xhr, textStatus, errorThrown) {
        // Here you are able now to access to the property "responseText"
        // as you have the type set to "text" instead of "blob".
        console.error(xhr.responseText);
    },
    success: function(data) {
        console.log(data); // Here is "blob" type
    }
});

Note

If you debug and place a breakpoint at the point right after setting the xhr.responseType to "blob" you can note that if you try to get the value for responseText you will get the following message:

The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').

informatik01
  • 16,038
  • 10
  • 74
  • 104
Alberto
  • 1,423
  • 18
  • 32
  • 4
    Thank you very much! This was the solution I was looking for. I needed AJAX to handle the successful response as "blob" (ZIP archive in my case) in the `.done()` method, and if something went wrong, the response in the `.fail()` method should be handled as "text", because otherwise the `responseText` was empty etc. This solution worked perfectly for me! – informatik01 Nov 21 '19 at 09:18
6

A big thank you to @Musa and here is a neat function that converts the data to a base64 string. This may come handy to you when handling a binary file (pdf, png, jpeg, docx, ...) file in a WebView that gets the binary file but you need to transfer the file's data safely into your app.

// runs a get/post on url with post variables, where:
// url ... your url
// post ... {'key1':'value1', 'key2':'value2', ...}
//          set to null if you need a GET instead of POST req
// done ... function(t) called when request returns
function getFile(url, post, done)
{
   var postEnc, method;
   if (post == null)
   {
      postEnc = '';
      method = 'GET';
   }
   else
   {
      method = 'POST';
      postEnc = new FormData();
      for(var i in post)
         postEnc.append(i, post[i]);
   }
   var xhr = new XMLHttpRequest();
   xhr.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200)
      {
         var res = this.response;
         var reader = new window.FileReader();
         reader.readAsDataURL(res); 
         reader.onloadend = function() { done(reader.result.split('base64,')[1]); }
      }
   }
   xhr.open(method, url);
   xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
   xhr.send('fname=Henry&lname=Ford');
   xhr.responseType = 'blob';
   xhr.send(postEnc);
}
mxl
  • 637
  • 7
  • 9