Question

I am trying to display a favicon.ico on a web page not as the shortcut icon, but as an image in the page's body. On our test server in IE, the images were failing to display, and we found out it was because the MIME-type configured on the server for the .ico file type was image/vnd.microsoft.icon instead of image/x-icon.

Now, we were able to reconfigure our server and fix the problem, but I was wondering if it is possible to specify which MIME type to use in the <img> tag and override server-wide settings for a specific file?

Était-ce utile?

La solution 2

There is no attribute for specifying a media type in an img element. If you use e.g. the object element instead (it works for images, too, but with some quirks), you can use the type attribute there. But its definition in HTML 4.01 says: “This attribute is optional but recommended when data is specified since it allows the user agent to avoid loading information for unsupported content types. If the value of this attribute differs from the HTTP Content-Type returned by the server when the object is retrieved, the HTTP Content-Type takes precedence.” In HTML5 CR, it’s a bit different, but the point still is that the type attribute is not expected to override HTTP headers—quite the contrary.

Autres conseils

The good news is that it is possible to forcibly override the MIME type of an image if you can't rely on the one provided by the server. The bad news is that it relies on Javascript, and is somewhat hacky.

Background

I wanted to use files stored in my Gitorious repositories in HTML image tags. However, the "raw" file data was labelled as text/plain by the server, which prevented Internet Explorer from displaying them. Firefox and Chrome worked fine, so I assume that they must ignore the provided MIME type and figure out the actual format based on the image data.

The Problem

You can't explicitly specify a MIME type for an <img> tag. For one thing, <img> tags don't have a type attribute (unlike <object> and <embed>, or the <source> tags used by <audio> and <video> elements). Even if they did, there's no guarantee that it would make any difference:

This specification does not currently say whether or how to check the MIME types of the media resources, or whether or how to perform file type sniffing using the actual file data. Implementors differ in their intentions on this matter and it is therefore unclear what the right solution is. In the absence of any requirement here, the HTTP specification's strict requirement to follow the Content-Type header prevails ("Content-Type specifies the media type of the underlying data." ... "If and only if the media type is not given by a Content-Type field, the recipient MAY attempt to guess the media type via inspection of its content and/or the name extension(s) of the URI used to identify the resource.").

A Possible Solution

The image data can be "manually" downloaded via an XMLHttpRequest. Once the actual data is available to Javascript, it can be inserted into a page via DOM manipulation. Here's an example HTML file:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello, World!</title>
    <script src="ieimgfix.js"></script>
  </head>
  <body>
    <img alt="cat" src="https://gitorious.org/vector/vector/raw/0797c6f8faad3426d33d3748b07abd8c77d475a7:bin/media/Floyd-Steinberg_algorithm-original.jpg">
    <img alt="apple" src="https://gitorious.org/nanijsore/nanijsore/raw/34b9aae73b5623b9971c8d98878fdbb2a0264476:image/apple.png">
  </body>
</html>

...and here's the contents of the ieimgfix.js file:

"use strict";

// This function is called when any image tag fails to load.
function fixMIME()
{

  var img = this;

  // First of all, try to guess the MIME type based on the file extension.
  var mime;
  switch (img.src.toLowerCase().slice(-4))
  {
    case ".bmp":              mime = "bmp";     break;
    case ".gif":              mime = "gif";     break;
    case ".jpg": case "jpeg": mime = "jpeg";    break;
    case ".png": case "apng": mime = "png";     break;
    case ".svg": case "svgz": mime = "svg+xml"; break;
    case ".tif": case "tiff": mime = "tiff";    break;
    default: console.log("Unknown file extension: " + img.src); return;
  }
  console.log("Couldn't load " + img.src + "; retrying as image/" + mime);

  // Attempt to download the image data via an XMLHttpRequest.
  var xhr = new XMLHttpRequest();
  xhr.onload = function()
  {
    if (this.status != 200) { return console.log("FAILED: " + img.src); }
    // Blob > ArrayBuffer: http://stackoverflow.com/a/15981017/4200092
    var reader = new FileReader();
    reader.onload = function()
    {
      // TypedArray > Base64 text: http://stackoverflow.com/a/12713326/4200092
      var data = String.fromCharCode.apply(null, new Uint8Array(this.result));
      img.src = "data:image/" + mime + ";base64," + btoa(data);
    };
    reader.readAsArrayBuffer(this.response);
  };
  xhr.open("get", this.src, true);
  xhr.responseType = "blob";
  xhr.send();

}

// This callback happens after the DOCUMENT is loaded but before IMAGES are.
document.addEventListener("readystatechange", function() {
  if (document.readyState != "interactive") { return; }
  // Add an error handler callback to all image tags in the document.
  var t = document.getElementsByTagName("img");
  for (var i = 0; i < t.length; ++i) { t[i].addEventListener("error", fixMIME, false); }
}, false);

Remember that any new <img> tags added to the page via DOM manipulation are not covered, so you would need to attach listeners to those yourself.

CORS

It's interesting to note that the code above causes both Firefox and Chrome complain about CORS when handling invalid image URLs:

Chrome:

XMLHttpRequest cannot load http://www.google.com/notarealfile.png. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 404.

Firefox:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://www.google.com/notarealfile.png. This can be fixed by moving the resource to the same domain or enabling CORS.

However, Internet Explorer 11 doesn't seem to care. This works out rather well:

  • Incorrect MIME types work fine in Chrome/Firefox, so no XMLHttpRequest needs to be made.
  • Incorrect MIME types won't work on Internet Explorer, but the XMLHttpRequest will run without CORS-related problems.

Disclaimer: The Right Thing To Do is to have the server send the correct MIME type. This is a rather hacky workaround, and I wouldn't recommend it unless you really have no other choice.

I have tried in the past; however, it has always been my experience that this must be configured on the server. Content within the pages should always stick to the standard file types to avoid future issues, such as .png, .gif, and .jpg. Just my two cents. Hope it helps.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top