Comment vérifier le type MIME de fichier avec javascript avant de télécharger?

j'ai lu ce et ce des questions qui semble suggérer que le type MIME du fichier peut être vérifié à l'aide de javascript côté client. Maintenant, je comprends que la vraie validation doit encore être faite du côté du serveur. Je veux effectuer une vérification côté client pour éviter le gaspillage inutile de la ressource du serveur.

pour tester si cela peut être fait du côté du client, j'ai changé l'extension d'un fichier test JPEG .png et choisissez le fichier à télécharger. Avant d'envoyer le fichier, j'interroge l'objet file en utilisant une console javascript:

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

C'est ce que J'obtiens sur Chrome 28.0:

Fichier {webkitRelativePath: "", lastModifiedDate le: Mar 16 Oct 2012 10: 00: 00 GMT+0000 (UTC), nom: "test.png", type: "image / png", taille: 500055...}

il montre que le type est image/png ce qui semble indiquer que la vérification se fait en fonction de l'extension du fichier au lieu du type MIME. J'ai essayé Firefox 22.0 et ça me donne le même résultat. Mais selon la spécification W3C , MIME Sniffing devrait être mise en œuvre.

ai-je raison de dire qu'il n'y a aucun moyen de vérifier le type MIME avec javascript pour le moment? Ou ai-je raté quelque chose?

115
demandé sur Community 2013-08-18 17:53:37

7 réponses

vous pouvez facilement déterminer le type MIME de fichier avec FileReader de JavaScript avant de le télécharger sur un serveur. Je suis d'accord que nous devrions préférer la vérification côté serveur à la vérification côté client, mais la vérification côté client est toujours possible. Je vais vous montrer comment et fournir une démo de travail en bas.


vérifiez que votre navigateur supporte à la fois File et Blob . Tous les grands devraient.

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

Step 1:

vous pouvez récupérer l'information File d'un élément <input> comme ceci ( ref ):

<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>

Voici une version glisser-déposer de ce qui précède ( ref ):

<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>

Étape 2:

nous pouvons maintenant inspecter les fichiers et extraire les en-têtes et les types MIME.

Méthode rapide "1519410920

vous pouvez naïvement demander Blob pour le type MIME de n'importe quel fichier qu'il représente en utilisant ce modèle:

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

pour les images, les types MIME reviennent comme suit:

image / jpeg

image / png

...

mise en garde: Le MIME le type est détecté à partir de l'extension du fichier et peut être dupé ou mystifié. On peut renommer un .jpg en .png et le type MIME sera déclaré comme image/png .


✓ en-tête approprié - méthode d'inspection

pour obtenir le type MIME bonafide d'un fichier côté client, nous pouvons aller un peu plus loin et inspecter les premiers octets du fichier donné à comparer avec ce que l'on appelle des numéros de magie . Soyez averti que ce n'est pas tout à fait simple parce que, par exemple, JPEG a quelques "nombres magiques". C'est parce que le format a évolué depuis 1991. Vous pourriez sortir avec vérifiant uniquement les deux premiers octets, mais je préfère vérifier au moins 4 octets pour réduire les faux positifs.

exemples de signatures de fichiers JPEG (4 premiers octets):

FF D8 FF E0 (SOI + ADD0)

FF D8 FF E1 (SOI + ADD1)

FF D8 FF E2 (SOI + ADD2)

voici le code essentiel pour récupérer l'en-tête de fichier:

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);

vous pouvez alors déterminer le type MIME réel comme so (plus de signatures de fichier ici et ici ):

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;
}

accepte ou rejette les téléchargements de fichiers selon les types MIME devrait.


Démo

Voici une démo qui fonctionne pour les fichiers locaux et (j'ai dû contourner CORS juste pour cette démo). Ouvrez le snippet, lancez-le, et vous devriez voir trois images à distance de différents types affichés. En haut, vous pouvez sélectionner une image locale ou fichier de données, et la signature du fichier et/ou le type MIME sera affiché.

Notez que même si une image est renommée, son vrai type MIME peut être déterminé. Voir ci-dessous.

Capture d'écran

Expected output of demo


// Return the first few bytes of the file as a hex string
function getBLOBFileHeader(url, blob, callback) {
  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);
    }
    callback(url, header);
  };
  fileReader.readAsArrayBuffer(blob);
}

function getRemoteFileHeader(url, callback) {
  var xhr = new XMLHttpRequest();
  // Bypass CORS for this demo - naughty, Drakes
  xhr.open('GET', '//cors-anywhere.herokuapp.com/' + url);
  xhr.responseType = "blob";
  xhr.onload = function() {
    callback(url, xhr.response);
  };
  xhr.onerror = function() {
    alert('A network error occurred!');
  };
  xhr.send();
}

function headerCallback(url, headerString) {
  printHeaderInfo(url, headerString);
}

function remoteCallback(url, blob) {
  printImage(blob);
  getBLOBFileHeader(url, blob, headerCallback);
}

function printImage(blob) {
  // Add this image to the document body for proof of GET success
  var fr = new FileReader();
  fr.onloadend = function() {
    $("hr").after($("<img>").attr("src", fr.result))
      .after($("<div>").text("Blob MIME type: " + blob.type));
  };
  fr.readAsDataURL(blob);
}

// Add more from http://en.wikipedia.org/wiki/List_of_file_signatures
function mimeType(headerString) {
  switch (headerString) {
    case "89504e47":
      type = "image/png";
      break;
    case "47494638":
      type = "image/gif";
      break;
    case "ffd8ffe0":
    case "ffd8ffe1":
    case "ffd8ffe2":
      type = "image/jpeg";
      break;
    default:
      type = "unknown";
      break;
  }
  return type;
}

function printHeaderInfo(url, headerString) {
  $("hr").after($("<div>").text("Real MIME type: " + mimeType(headerString)))
    .after($("<div>").text("File header: 0x" + headerString))
    .after($("<div>").text(url));
}

/* Demo driver code */

var imageURLsArray = ["http://media2.giphy.com/media/8KrhxtEsrdhD2/giphy.gif", "http://upload.wikimedia.org/wikipedia/commons/e/e9/Felis_silvestris_silvestris_small_gradual_decrease_of_quality.png", "http://static.giantbomb.com/uploads/scale_small/0/316/520157-apple_logo_dec07.jpg"];

// Check for FileReader support
if (window.FileReader && window.Blob) {
  // Load all the remote images from the urls array
  for (var i = 0; i < imageURLsArray.length; i++) {
    getRemoteFileHeader(imageURLsArray[i], remoteCallback);
  }

  /* Handle local files */
  $("input").on('change', function(event) {
    var file = event.target.files[0];
    if (file.size >= 2 * 1024 * 1024) {
      alert("File size must be at most 2MB");
      return;
    }
    remoteCallback(escape(file.name), file);
  });

} else {
  // File and Blob are not supported
  $("hr").after( $("<div>").text("It seems your browser doesn't support FileReader") );
} /* Drakes, 2015 */
img {
  max-height: 200px
}
div {
  height: 26px;
  font: Arial;
  font-size: 12pt
}
form {
  height: 40px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<form>
  <input type="file" />
  <div>Choose an image to see its file signature.</div>
</form>
<hr/>
226
répondu Drakes 2018-01-30 23:11:51

comme indiqué dans d'autres réponses, vous pouvez vérifier le type mime en cochant la signature du fichier dans les premiers octets du fichier.

mais ce que les autres réponses font est chargement du fichier entier en mémoire afin de vérifier la signature, qui est très gaspilleuse et pourrait facilement geler votre navigateur si vous sélectionnez un grand fichier par accident ou non.

/**
 * Load the mime type based on the signature of the first bytes of the file
 * @param  {File}   file        A instance of File
 * @param  {Function} callback  Callback with the result
 * @author Victor www.vitim.us
 * @date   2017-03-23
 */
function loadMime(file, callback) {
    
    //List of known mimes
    var mimes = [
        {
            mime: 'image/jpeg',
            pattern: [0xFF, 0xD8, 0xFF],
            mask: [0xFF, 0xFF, 0xFF],
        },
        {
            mime: 'image/png',
            pattern: [0x89, 0x50, 0x4E, 0x47],
            mask: [0xFF, 0xFF, 0xFF, 0xFF],
        }
        // you can expand this list @see https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
    ];

    function check(bytes, mime) {
        for (var i = 0, l = mime.mask.length; i < l; ++i) {
            if ((bytes[i] & mime.mask[i]) - mime.pattern[i] !== 0) {
                return false;
            }
        }
        return true;
    }

    var blob = file.slice(0, 4); //read the first 4 bytes of the file

    var reader = new FileReader();
    reader.onloadend = function(e) {
        if (e.target.readyState === FileReader.DONE) {
            var bytes = new Uint8Array(e.target.result);

            for (var i=0, l = mimes.length; i<l; ++i) {
                if (check(bytes, mimes[i])) return callback("Mime: " + mimes[i].mime + " <br> Browser:" + file.type);
            }

            return callback("Mime: unknown <br> Browser:" + file.type);
        }
    };
    reader.readAsArrayBuffer(blob);
}


//when selecting a file on the input
fileInput.onchange = function() {
    loadMime(fileInput.files[0], function(mime) {

        //print the output to the screen
        output.innerHTML = mime;
    });
};
<input type="file" id="fileInput">
<div id="output"></div>
4
répondu Vitim.us 2018-07-06 16:34:17

si vous voulez juste vérifier si le fichier téléchargé est une image, vous pouvez juste essayer de le charger dans <img> tag une vérification pour toute erreur de rappel.

exemple:

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;
}
3
répondu Roberto14 2016-05-20 08:40:20

comme Drake dit que cela pourrait être fait avec FileReader. Cependant, ce que je présente ici est une version fonctionnelle. Prenez en considération que le gros problème avec JavaScript est de réinitialiser le fichier d'entrée. Eh bien, cela ne concerne que le JPG (pour les autres formats, vous devrez changer le type mime et le numéro magique ):

<form id="form-id">
  <input type="file" id="input-id" accept="image/jpeg"/>
</form>

<script type="text/javascript">
    $(function(){
        $("#input-id").on('change', function(event) {
            var file = event.target.files[0];
            if(file.size>=2*1024*1024) {
                alert("JPG images of maximum 2MB");
                $("#form-id").get(0).reset(); //the tricky part is to "empty" the input file here I reset the form.
                return;
            }

            if(!file.type.match('image/jp.*')) {
                alert("only JPG images");
                $("#form-id").get(0).reset(); //the tricky part is to "empty" the input file here I reset the form.
                return;
            }

            var fileReader = new FileReader();
            fileReader.onload = function(e) {
                var int32View = new Uint8Array(e.target.result);
                //verify the magic number
                // for JPG is 0xFF 0xD8 0xFF 0xE0 (see https://en.wikipedia.org/wiki/List_of_file_signatures)
                if(int32View.length>4 && int32View[0]==0xFF && int32View[1]==0xD8 && int32View[2]==0xFF && int32View[3]==0xE0) {
                    alert("ok!");
                } else {
                    alert("only valid JPG images");
                    $("#form-id").get(0).reset(); //the tricky part is to "empty" the input file here I reset the form.
                    return;
                }
            };
            fileReader.readAsArrayBuffer(file);
        });
    });
</script>

prendre en considération que cela a été testé sur les dernières versions de Firefox et Chrome, et sur IExplore 10.

pour une liste complète des types mime voir Wikipedia .

pour une liste complète des nombres magiques, voir Wikipedia .

1
répondu lmiguelmh 2017-06-13 22:56:47

réponse Courte est non.

comme vous le constatez, les navigateurs dérivent type de l'extension du fichier. Mac preview semble également lancer l'extension. Je suppose que c'est parce que c'est plus rapide de lire le nom du fichier contenu dans le pointeur, plutôt que de chercher et de lire le fichier sur le disque.

j'ai fait une copie d'un jpg renommé avec png.

j'ai été en mesure d'obtenir de façon constante les suivants à partir des deux images dans chrome (devrait fonctionner dans les navigateurs modernes).

ÿØÿàJFIFÿþ;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90

que vous pourriez découper une chaîne.indexOf ('jpeg') vérifier le type d'image.

voici un violon pour explorer http://jsfiddle.net/bamboo/jkZ2v/1 /

la ligne ambiguë j'ai oublié de commenter dans l'exemple

console.log( /^(.*)$/m.exec(window.atob( image.src.split(',')[1] )) );

  • divise les données codées img base64, laissant sur le image
  • Base64 décode l'image
  • ne correspond qu'à la première ligne des données d'image

le code fiddle utilise base64 decode qui ne fonctionnera pas dans IE9, j'ai trouvé un bel exemple en utilisant le script VB qui fonctionne dans IE http://blog.nihilogic.dk/2008/08/imageinfo-reading-image-metadata-with.html

le code pour charger l'image a été pris de Joel Vardy, qui fait quelques image fraîche toile redimensionner le côté client avant de télécharger ce qui peut être intéressant https://joelvardy.com/writing/javascript-image-upload

0
répondu Lex 2014-06-26 02:35:35

voici une extension de la réponse de Roberto14 qui fait ce qui suit:

CELA N'AUTORISE QUE LES IMAGES

vérifie si FileReader est disponible et retourne à la vérification d'extension si elle n'est pas disponible.

donne une alerte d'erreur si non une image

si c'est une image elle charge un aperçu

* * vous devriez toujours faire la validation côté serveur, c'est plus pratique pour le l'utilisateur final qu'autre chose. Mais il est à portée de main!

<form id="myform">
    <input type="file" id="myimage" onchange="readURL(this)" />
    <img id="preview" src="#" alt="Image Preview" />
</form>

<script>
function readURL(input) {
    if (window.FileReader && window.Blob) {
        if (input.files && input.files[0]) {
            var reader = new FileReader();
            reader.onload = function (e) {
                var img = new Image();
                img.onload = function() {
                    var preview = document.getElementById('preview');
                    preview.src = e.target.result;
                    };
                img.onerror = function() { 
                    alert('error');
                    input.value = '';
                    };
                img.src = e.target.result;
                }
            reader.readAsDataURL(input.files[0]);
            }
        }
    else {
        var ext = input.value.split('.');
        ext = ext[ext.length-1].toLowerCase();      
        var arrayExtensions = ['jpg' , 'jpeg', 'png', 'bmp', 'gif'];
        if (arrayExtensions.lastIndexOf(ext) == -1) {
            alert('error');
            input.value = '';
            }
        else {
            var preview = document.getElementById('preview');
            preview.setAttribute('alt', 'Browser does not support preview.');
            }
        }
    }
</script>
0
répondu pathfinder 2016-09-18 16:48:42

c'est Ce que vous avez à faire

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

si vous voulez vérifier les types de fichiers image alors

if(fileVariable.type.match('image.*'))
{
 alert('its an image');
}
0
répondu Kailas 2017-03-14 07:15:43