Dai
Dai

Reputation: 155135

Do we *have* to use data: URIs (readAsDataURL) to use File objects with <img /> in JavaScript?

I understand the current common technique for creating an <img /> element in JavaScript from a client-side File object goes something like this.

(The File object would originate from a file drag-and-drop handler or <input type="file" /> change event.)

function showImageFile( file: File ): void {

    let rdr = new FileReader();
    rdr.readAsDataURL( file );
    rdr.onloadend = function() {
        let img = document.createElement('img');
        img.src = rdr.result;
        document.body.appendChild( img );
    };
}

I don't like this - the notion of serialising what could be a multi-megabyte image to a base64-encoded string (thus using 1.3x more memory on top of the existing File object) as well as incurring the cost of deserializing the string back into the <img /> element - ultimately this will cause at least 3 instances of the image's data to exist in memory. This seems very wasteful and inefficient to me, and JavaScript applications already have a bad rep for excessive memory consumption.

I noticed that for the <video> and <audio> elements (HTMLMediaElement) that MDN recommends doing something like this:

function showVideoFile( file: File ): void {

    let video = document.createElement('video');
    video.srcObject = URL.createObjectURL( file ); // createObjectURL creates a URI alias to the existing file instance in-memory
}

(with the caveat that if srcObject is ever replaced or the video element removed, the previous objectURL must be manually released using revokeObjectURL.

Is there any way to do the same thing for <img />? (I don't see a srcObject property on HTMLImageElement and HTMLImageElement does not derive from HTMLMediaElement). Searching online for "javascript image files without data URIs" unfortunately only returns pages about readAsDataURL which is the opposite of what I want.

Upvotes: 1

Views: 540

Answers (2)

Dai
Dai

Reputation: 155135

After sacrificing more minutes down dark and dirty information mines at Google I found a nugget of coal in this December 2014 article http://bytes.schibsted.com/the-magic-of-createobjecturl/ that says that the HTMLImageElement's src attribute does actually accept an object URI (phew!)

MDN also cover it on their website, albeit buried somewhat: https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications#Example_Using_object_URLs_to_display_images

In short, my showImageFile function can be updated like so:

function showImageFile( file: File ): void {

    let objectURI = URL.createObjectURL( file ); // looks like "blob:http://mywebsite/{guid}"

    let img = document.createElement('img');
    img.src = objectURI ;
    document.body.appendChild( img );
}

Or if the img element will be reused:

var img: HTMLImageElement;

function showImageFile( file: File ): void {

    let oldSrc = img.src;

    let objectURI = URL.createObjectURL( file );

    img.src = objectURI;

    if( oldSrc ) URL.revokeObjectURL( oldSrc );
}

Upvotes: 0

Ry-
Ry-

Reputation: 224904

You set its… src.

document.getElementById('file').onchange = function () {
    if (this.files.length !== 0) {
        var img = new Image();
        img.src = URL.createObjectURL(this.files[0]);
        document.body.appendChild(img);
    }
};
<input type="file" id="file" accept="image/*" />

Upvotes: 3

Related Questions