10

This should be simple enough, but after wrestling with it for hours, I still can't get it to work. So far, all my attempts have resulted in the image becoming 'corrupted or truncated', according to firefox.

Retrieve the image from the server with a jquery-ajax call:

 $.ajax({
                async: false,
                url: db[key]["DocumentLink"],
                success: function (result2) {


Base64 encode the image, and store it in localStore:
In this example I'm using the jquery base64-encoding plugin, but I've tried several.

                        var dbKey = "Doc " + db[key]["ID"] + " " + db[key]["Title"];
                        console.log("storing: " + db[key]["DocumentLink"] + " in " + dbKey + "\n");
                        localStorage.removeItem(dbKey);
                        var base64Image = $.base64Encode(result2);
                        console.log(base64Image.length);
                        localStorage.setItem(dbKey, base64Image);
                       console.log("is stored: " + db[key]["DocumentLink"] + " in " + dbKey + "\n");
                }
})


Display the image with a data url:

function openImageFromDB(dbKey) {
    console.log("Trying to display image with key " + dbKey);
    var base64Img = localStorage.getItem(dbKey);
    document.getElementById("documentHolder").src='data:image/jpeg;base64,' + base64Img;
}


The corresponding img:

 <img id="documentHolder" alt="Image view placeholder" src="" />


However, on every try, firefox displays:

Image corrupt or truncated: data:image/jpeg;base64,77+977+977+977+9a<... much longer string>


The Url: points to a valid jpeg image, and the base64Image.length and the error message show that the var / localStorage actually contain what seems to be base64 encoded data.

Any ideas?

Gumbo
  • 643,351
  • 109
  • 780
  • 844
TinkerTank
  • 5,685
  • 2
  • 32
  • 41

4 Answers4

9

Javascript (AJAX call)

function LoadImg(filename) {
    var xmlhttp;
    if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp = new XMLHttpRequest();
    } else { // code for IE6, IE5
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {     
            document.getElementById("documentHolder").src = "data:image/png;base64," + xmlhttp.responseText;
        }
    };    
    xmlhttp.open("GET", 'load.php?LoadImg='+filename );
    xmlhttp.send(null);
}

PHP ( load.php )

<?php
 if (isset($_GET['LoadImg'])) {
  header("Content-Type: image/png");
  $file = file_get_contents($_GET['LoadImg']);
  echo base64_encode($file);
}
?>

Read this may help you:

PS: maybe your Base64 is wrong?

Luca Filosofi
  • 30,905
  • 9
  • 70
  • 77
  • Thank you for your concise answer. Please note that, in this way, the image is still being converted to base64 on the server-side. I would like to convert it on the client-side, but I'm beginning to believe that the image might get mangled in the process of the AJAX-call somehow.
    p.s. - I am using asp.net, but I should definitely be able to find out a way to encode base64 images server-side. I would just like to be less dependent on server-side code.
    – TinkerTank Mar 19 '11 at 22:40
  • @TumbleCow: you should be definitely less dependent on the client-side than the server-side. every process maked on server-side is more faster and secure then the client-side. ;) – Luca Filosofi Mar 19 '11 at 22:56
6

Browsers have size limitations other than the localStorage limit of 5MB. The data for the <img src="data:image/jpeg;base64,..."> is also restricted and is usually much less than 5MB. The easiest way around this is to just pass the file-names via localStorage and let the browsers caching do the work.

j0k
  • 22,600
  • 28
  • 79
  • 90
bartonlp
  • 123
  • 1
  • 6
2

Turns out, AJAX can't be used to reliably transfer binary data. The solution is to run the Base64 encoding server-side, and transfer the resulting string through AJAX.

The above php-code works. For whoever is looking for a ASP.Net / C# solution:

    public string Image(string relpath)
    {
        Response.ContentType = "image/jpeg";

        string base64;
        string filename = Request.PhysicalApplicationPath + relpath;
        try
        {
            using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
            {
                var buffer = new byte[fs.Length];
                fs.Read(buffer, 0, (int)fs.Length);
                base64 = Convert.ToBase64String(buffer);
            }
        }
        catch (IOException e)
        {
            return filename + " / " + e.Message;
        }
        return base64;
    }

Please note that this code is NOT SECURE to expose directly to the outer world. I personally use a wrapper-function that parses the path from a db.

TinkerTank
  • 5,685
  • 2
  • 32
  • 41
0

You can get the data uri (which will contain the base 64 encoding) via JavaScript using the HTML5 canvas element.

        // I'm assuming here you've put the image in an <img> tag on the page already.
        // If not, you'll need to adapt this a bit, or perhaps this approach is just
        // not right for your situation.
        var image = document.getElementById('id-of-image-you-want');

        var canvas = w.document.createElement("canvas"); 
        canvas.width = image.width;
        canvas.height = image.height;

        var ctx = canvas.getContext("2d");
        ctx.drawImage(image, 0, 0);
        try {
            var dataUri = canvas.toDataURL();    
        } catch (e) {
            console.log("D'oh!"); // Improve this error handling, obviously.
        }

dataUri will now contain the data uri for the image which will contain, along with a short prefix, the base 64 encoding of the image.

Be sure to add detection for canvas support. IE 8 and older do not support it.

Trott
  • 66,479
  • 23
  • 173
  • 212