J'ai lu this et this des questions qui semblent suggérer que le type de fichier MIME pourrait être vérifié à l'aide de javascript du côté client. Maintenant, je comprends que la vraie validation doit encore être faite côté serveur. Je souhaite effectuer une vérification côté client pour éviter un gaspillage inutile des ressources du serveur.
Pour vérifier si cela peut être fait côté client, j'ai changé l'extension d'un fichier de test JPEG
en .png
et j'ai choisi le fichier à télécharger. Avant d'envoyer le fichier, j'interroge l'objet fichier à l'aide d'une console javascript:
document.getElementsByTagName('input')[0].files[0];
Voici ce que je lis sur Chrome 28.0:
Fichier {webkitRelativePath: "", lastModifiedDate: mardi 16 oct. 2012 10:00:00 GMT + 0000 (UTC), nom: "test.png", type: "image/png", taille: 500055…}
Il indique que le type est image/png
, ce qui semble indiquer que la vérification est effectuée en fonction de l'extension de fichier au lieu du type MIME. J'ai essayé Firefox 22.0 et cela me donne le même résultat. Mais selon les spécifications du W3C , MIME Sniffing devrait être implémenté.
Ai-je raison de dire qu'il n'y a aucun moyen de vérifier le type MIME avec javascript pour le moment? Ou est-ce que je manque quelque chose?
Vous pouvez facilement déterminer le type de fichier MIME avec le code FileReader
de JavaScript avant de le télécharger sur un serveur. Je conviens que nous devrions préférer la vérification côté serveur plutôt que 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 au bas.
Vérifiez que votre navigateur prend en charge File
et Blob
. Tous les principaux devraient.
if (window.FileReader && window.Blob) {
// All the File APIs are supported.
} else {
// File and Blob are not supported
}
Vous pouvez récupérer les informations 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>
Nous pouvons maintenant inspecter les fichiers et extraire les en-têtes et les types MIME.
method Méthode rapide
Vous pouvez naïvement demander Blob pour le type MIME du 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
...
Avertissement: Le type MIME est détecté à partir de l'extension de fichier et peut être dupé ou falsifié. On peut renommer un .jpg
en un .png
et le type MIME sera signalé par image/png
.
✓ Méthode d’inspection d’en-tête appropriée
Pour obtenir le type MIME de bonne foi d'un fichier côté client, nous pouvons aller plus loin et inspecter les premiers octets du fichier donné pour les comparer à ce qu'on appelle nombres magiques . Soyez averti que ce n'est pas tout à fait simple car, par exemple, JPEG a quelques "nombres magiques". En effet, le format a évolué depuis 1991. Vous pouvez vous contenter de vérifier uniquement les deux premiers octets, mais je préfère vérifier au moins 4 octets pour réduire les faux positifs.
Exemple 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 du 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 réel de MIME comme suit (plus de signatures de fichiers 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;
}
Acceptez ou refusez les téléchargements de fichiers à votre guise en fonction des types MIME attendus.
Voici une démo de travail pour les fichiers locaux et les fichiers distants (j'ai dû contourner CORS uniquement pour cette démo). Ouvrez l'extrait de code, exécutez-le et vous devriez voir trois images distantes de types différents affichées. En haut, vous pouvez sélectionner une image locale ou fichier de données. La signature du fichier et/ou le type MIME seront affichés.
Notez que même si une image est renommée, son vrai type MIME peut être déterminé. Voir ci-dessous.
Capture d'écran
// 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/>
Comme indiqué dans d'autres réponses, vous pouvez vérifier le type MIME en vérifiant le signature du fichier dans les premiers octets du fichier.
Mais ce que les autres réponses sont en train de faire, c’est de charger le fichier entier en mémoire afin de vérifier la signature, ce qui est très inutile et pourrait facilement geler votre navigateur. sélectionnez un gros 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>
Si vous voulez juste vérifier si le fichier téléchargé est une image, vous pouvez simplement essayer de le charger dans la balise <img>
et vérifier si un rappel d'erreur se produit.
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;
}
Sindresorhus a créé un utilitaire qui fonctionne dans le navigateur et propose des mappages d’en-tête à mime pour la plupart des documents souhaités.
https://github.com/sindresorhus/file-type
Vous pouvez combiner la suggestion de Vitim.us de ne lire que les X premiers octets pour éviter de tout charger en mémoire avec l'utilisation de cet utilitaire (exemple dans es6):
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(bytes);
// ext is the desired extension and mime is the mimetype
};
reader.readAsArrayBuffer(blob);
C'est ce que tu dois faire
var fileVariable =document.getElementsById('fileId').files[0];
Si vous voulez vérifier les types de fichiers image, puis
if(fileVariable.type.match('image.*'))
{
alert('its an image');
}
Comme Drake le dit, cela pourrait être fait avec FileReader. Cependant, ce que je présente ici est une version fonctionnelle. Notez que le gros problème avec JavaScript est de réinitialiser le fichier d’entrée. Eh bien, cela se limite à JPG (pour les autres formats, vous devrez changer le type mime et le nombre 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>
Notez 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 de mime, voir Wikipedia .
Pour une liste complète des nombres magiques, voir Wikipedia .
La réponse courte est non.
Comme vous le constatez, les navigateurs dérivent type
de l’extension de fichier. L'aperçu Mac semble également fonctionner à partir de l'extension. Je suppose que c'est parce que c'est plus rapide de lire le nom de 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 pu obtenir de manière constante les éléments suivants des deux images dans chrome (devrait fonctionner dans les navigateurs modernes).
ÿØÿàJFIFÿþ;CREATOR: Gd-jpeg v1.0 (using IJG JPEG v62), quality = 90
Ce que vous pourriez pirater une vérification String.indexOf ('jpeg') pour le type d'image.
Voici un violon à explorer http://jsfiddle.net/bamboo/jkZ2v/1/
La ligne ambiguë que j'ai oublié de commenter dans l'exemple
console.log( /^(.*)$/m.exec(window.atob( image.src.split(',')[1] )) );
Le code de violon utilise le décodage en base64, ce qui ne fonctionnera pas dans IE9. J'ai trouvé un exemple intéressant utilisant un script VB fonctionnant dans IE http://blog.nihilogic.dk /2008/08/imageinfo-reading-image-metadata-with.html
Le code pour charger l’image a été emprunté à Joel Vardy, qui est en train de redimensionner le canevas de l’image avant le téléchargement, ce qui peut présenter un intérêt https://joelvardy.com/writing/javascript-image-upload =
Voici une extension de la réponse de Roberto14 qui fait ce qui suit:
CECI NE PERMET QUE DES IMAGES
Vérifie si FileReader est disponible et revient à la vérification d'extension si elle n'est pas disponible.
Donne une alerte d'erreur s'il ne s'agit pas d'une image
Si c'est une image, il charge un aperçu
** Vous devez toujours effectuer la validation côté serveur, ceci est plus pratique pour l'utilisateur final qu'autre chose. Mais c'est pratique!
<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>