Coder Perfect

How can I use javascript to check the MIME type of a file before uploading it?

Problem

I read this and this queries, and it appears that the file MIME type might be checked on the client side using javascript. Now I understand that the true validation must take place on the server. To avoid wasting server resources, I’d like to do some client-side checks.

To test whether this can be done on client side, I changed the extension of a JPEG test file to .png and choose the file for upload. I use a javascript console to query the file object before transmitting it:

document.getElementsByTagName('input')[0].files[0];

On Chrome 28.0, I get the following:

It shows type as image/png, implying that the testing is done on the basis of file extension rather than MIME type. I also tried Firefox 22.0, which produced the same outcome. MIME Sniffing, however, is required by the W3C specification.

Is it correct that there is currently no mechanism to check the MIME type with javascript? Is there something I’m overlooking?

Asked by Question Overflow

Solution #1

Before uploading a file to a server, you can quickly determine the file’s MIME type using JavaScript’s FileReader. I agree that server-side checking should be preferred over client-side checking, but client-side checking is still doable. I’ll show you how and give you a working example at the end.

Make sure your browser can handle both File and Blob. All of the key ones should be.

if (window.FileReader && window.Blob) {
    // All the File APIs are supported.
} else {
    // File and Blob are not supported
}

An input> element like this (ref) can be used to get File information.

<input type="file" id="your-files" multiple>
<script>
var control = document.getElementById("your-files");
control.addEventListener("change", function(event) {
    // When the control has changed, there are new files
    var files = control.files,
    for (var i = 0; i < files.length; i++) {
        console.log("Filename: " + files[i].name);
        console.log("Type: " + files[i].type);
        console.log("Size: " + files[i].size + " bytes");
    }
}, false);
</script>

The following (ref) is a drag-and-drop variation of the above:

<div id="your-files"></div>
<script>
var target = document.getElementById("your-files");
target.addEventListener("dragover", function(event) {
    event.preventDefault();
}, false);

target.addEventListener("drop", function(event) {
    // Cancel default actions
    event.preventDefault();
    var files = event.dataTransfer.files,
    for (var i = 0; i < files.length; i++) {
        console.log("Filename: " + files[i].name);
        console.log("Type: " + files[i].type);
        console.log("Size: " + files[i].size + " bytes");
    }
}, false);
</script>

We can now look at the files and see what headers and MIME types they contain.

✘ Quick method

Using this technique, you can naively query Blob for the MIME type of whatever file it represents:

var blob = files[i]; // See step 1 above
console.log(blob.type);

The following MIME types are returned for images:

The MIME type is determined by the file extension and can be faked or misled. When a.jpg is renamed to a.png, the MIME type is reported as image/png.

Method for properly checking headers

To determine the true MIME type of a client-side file, we can examine the first few bytes of the file and compare them to so-called magic numbers. It’s important to note that it’s not fully clear, as JPEG, for example, includes a few “magic numbers.” This is due to the fact that the format has changed since 1991. You could check simply the first two bytes, but I prefer to check at least four bytes to avoid false positives.

Example JPEG file signatures (initial four bytes):

The following is the required code to retrieve the file header:

var blob = files[i]; // See step 1 above
var fileReader = new FileReader();
fileReader.onloadend = function(e) {
  var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
  var header = "";
  for(var i = 0; i < arr.length; i++) {
     header += arr[i].toString(16);
  }
  console.log(header);

  // Check the file signature against known types

};
fileReader.readAsArrayBuffer(blob);

The genuine MIME type can then be determined as follows (additional file signatures here and here):

switch (header) {
    case "89504e47":
        type = "image/png";
        break;
    case "47494638":
        type = "image/gif";
        break;
    case "ffd8ffe0":
    case "ffd8ffe1":
    case "ffd8ffe2":
    case "ffd8ffe3":
    case "ffd8ffe8":
        type = "image/jpeg";
        break;
    default:
        type = "unknown"; // Or you can use the blob.type as fallback
        break;
}

Accept or reject file uploads according to the MIME types that are expected.

Here’s a functioning example for both local and remote files (I had to bypass CORS just for this demo). When you run the snippet, you should see three different sorts of remote images. Select a local image or data file from the menu at the top, and the file signature and/or MIME type will be displayed.

It’s worth noting that an image’s true MIME type can be determined even if it’s renamed. See the list below.

Screenshot

Answered by Drakes

Solution #2

You may check the mime type by looking at the file’s signature in the first bytes, as described in earlier responses.

Other replies, on the other hand, load the full file into memory to check the signature, which is quite inefficient and can easily cause your browser to freeze if you select a large file by accident or not.

Answered by Vitim.us

Solution #3

Sindresorhus has created a browser utility that provides the header-to-mime mappings for most documents you could desire if you don’t want to build it yourself.

https://github.com/sindresorhus/file-type

You could use this utility (example in es6) in conjunction with Vitim.us’s recommendation of only reading the first X bytes to avoid putting everything into memory:

import fileType from 'file-type'; // or wherever you load the dependency

const blob = file.slice(0, fileType.minimumBytes);

const reader = new FileReader();
reader.onloadend = function(e) {
  if (e.target.readyState !== FileReader.DONE) {
    return;
  }

  const bytes = new Uint8Array(e.target.result);
  const { ext, mime } = fileType.fromBuffer(bytes);

  // ext is the desired extension and mime is the mimetype
};
reader.readAsArrayBuffer(blob);

Answered by Vinay

Solution #4

If you merely want to see if the file uploaded is an image, try loading it into the img> tag and looking for any error callbacks.

Example:

var input = document.getElementsByTagName('input')[0];
var reader = new FileReader();

reader.onload = function (e) {
    imageExists(e.target.result, function(exists){
        if (exists) {

            // Do something with the image file.. 

        } else {

            // different file format

        }
    });
};

reader.readAsDataURL(input.files[0]);


function imageExists(url, callback) {
    var img = new Image();
    img.onload = function() { callback(true); };
    img.onerror = function() { callback(false); };
    img.src = url;
}

Answered by Roberto14

Solution #5

This is exactly what you must do.

var fileVariable =document.getElementsById('fileId').files[0];

If you wish to see what picture file types are available, go here.

if(fileVariable.type.match('image.*'))
{
 alert('its an image');
}

Answered by Kailas

Post is based on https://stackoverflow.com/questions/18299806/how-to-check-file-mime-type-with-javascript-before-upload