1

In my web app, I'm having bunch of URLs types by users. My view is showing either link icon or image preview, depending on whether URL points to an image or not.

What is the most reliable way to display image previews only if it's really an image? Should I create <img> and check dimensions on load, use new Image() object, or maybe something else?

Any advice highly appreciated! :)

Robo Robok
  • 21,132
  • 17
  • 68
  • 126
  • Can the backend do that job for you? – VisioN Jun 17 '16 at 08:56
  • I would prefer not, because those URLs are in a feed file that can change anytime. I would love no caching behind it. I could use Ajax calls though, but it would increase bandwich - reading image once for parsing and once for viewing. – Robo Robok Jun 17 '16 at 08:58
  • I guess your feed file should contain the information whether the file is an image or not, and depending on that the frontend should display the data accordingly. Anyways, the `new Image` object supports [`onerror`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror) event, which can be used to manipulate the output. Alternatively, I may try to do a hack with a `background-image` style for the link to get rid of JavaScript at all. – VisioN Jun 17 '16 at 09:02
  • This feed file is third party and I need those images only for preview purpose. – Robo Robok Jun 17 '16 at 09:03
  • Can't you just check if the url your user types ends with a image extention such as 'png' or 'jpg'? If the URL points to the actual location of an image, then you can expect the URL to it to end with the images full name. – Glubus Jun 17 '16 at 09:16
  • 1
    @Glubus This is naive way to handle images. File extension doesn't imply content type, especially online. Why can't my image be under, let's say, `/flower.php?size=100x100`? – Robo Robok Jun 17 '16 at 09:21
  • @RoboRobok Clearly this is possible, but considering the nature of the feature, I'd say a best effort approach could be good enough. Most images are stored and retrieved directly. – Glubus Jun 17 '16 at 09:27

1 Answers1

1

The most reliable and compatible way to check if the source point is a valid image is to use the Image object. If it can load it as an image onload will trigger, if not onerror will trigger:

var img = new Image;
img.onload  = function() { /* OK */ };
img.onerror = function() { /* cannot read this as an image */ };
img.onabort = function() { /* connection or source was reset */ };
img.src = "URLtoCheckHere";

The drawback is of course that the whole image would be loaded before you could find out if the browser can read and decode it.

If Image for some reason can't be used you could read the source point via XHR but that comes with its own restrictions, one being cross-origin resource sharing. If the image is not of the same origin (protocol, domain, port etc.) and the external server does not allow cors-usage, reading would simply fail.

However, if these limitations aren't a problem (i.e. the images comes from the same server as the page or a cors-friendly site) then you could read in part of the image as ArrayBuffer and check the magic numbers for the image types you want to support. Be aware of byte-order.

To specify a range use the Range header (it's not sure the server will respect it though).

Assuming the part of the file has been read into an ArrayBuffer:

var reader = new DataView(arrBuffer);

if (reader.getUint32(0) === 0x89504E47 && 
    reader.getUint32(4) === 0x0D0A1A0A) {
  // this seem to be a PNG file
}
else ... etc.

But, even if the file is detected as a valid image file, there is no guarantee of that the file is not corrupt and so forth. There is no way to validate the file using this method (unless you chose to write a parser yourselves).

So in conclusion, Image is the best option in most scenarios. It has been through several iterations over the years in the main browsers and is pretty robust and stable.

Community
  • 1
  • 1
  • are you saying that CORS affects XHR and does not Image()? – vitr Jun 17 '16 at 09:48
  • 1
    It does not affect Image no (loading and displaying). You can read any url using Image. CORS will strike however if you draw that image to a canvas and then try to read out the data, but for display purposes no. I made a small demo here: https://jsfiddle.net/epistemex/ss36e0r4/ –  Jun 17 '16 at 09:54
  • 1
    I decided to use this: `$("").attr("src", src).load(...)`. I always thought JavaScript's `Image` is something else than ``, but I just realized it's not :) I don't need error handler in this case, so generated `` with src and `load()` is what I needed. It works! – Robo Robok Jun 17 '16 at 09:56
  • And I believe jQuery's `` has more potential to be cross-browser than `new Image`. – Robo Robok Jun 17 '16 at 09:57
  • @vitr It would be weird if CORS by standard didn't allow to display images from other domains. That wouldn't be web anymore. – Robo Robok Jun 17 '16 at 09:58
  • 1
    @RoboRobok exellent. Image is pretty universal, an alternative could be `document.createElement("img")`, but jQuery's version will do just fine. –  Jun 17 '16 at 09:59
  • sorry, I mixed it with HTML5 canvas, @K3N thanks for the excellent demo – vitr Jun 17 '16 at 11:16