Vous pouvez facilement déterminer le type MIME du fichier avec la fonction JavaScript FileReader
avant de le télécharger sur un serveur. Je suis d'accord pour dire 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 vous montrerai comment faire et vous fournirai une démonstration fonctionnelle en bas de page.
Vérifiez que votre navigateur prend en charge les deux File
y Blob
. Toutes les grandes entreprises devraient le faire.
if (window.FileReader && window.Blob) {
// All the File APIs are supported.
} else {
// File and Blob are not supported
}
Étape 1 :
Vous pouvez récupérer le File
des informations provenant d'un <input>
comme ceci ( réf. ) :
<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 drag-and-drop de l'image ci-dessus ( réf. ) :
<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 en extraire les en-têtes et les types MIME.
✘ Méthode rapide
Vous pouvez demander naïvement 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
...
Attention : Le type MIME est détecté à partir de l'extension du fichier et peut être trompé ou usurpé. On peut renommer un fichier .jpg
à un .png
et le type MIME sera signalé comme étant image/png
.
✓ Méthode appropriée d'inspection des en-têtes
Pour obtenir le véritable type MIME d'un fichier côté client, nous pouvons aller un peu plus loin et inspecter les premiers octets du fichier donné pour le comparer à ce que l'on appelle chiffres magiques . Sachez que ce n'est pas tout à fait simple car, par exemple, JPEG a quelques "chiffres magiques". Cela s'explique par le fait que le format a évolué depuis 1991. Vous pouvez vous en sortir en ne vérifiant que 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 véritable type MIME comme suit (plus de signatures de fichiers) aquí y aquí ) :
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 comme vous le souhaitez en fonction des types MIME attendus.
Démo
Voici une démonstration fonctionnelle pour les fichiers locaux y des fichiers distants (j'ai dû contourner CORS juste pour cette démo). Ouvrez le snippet, exécutez-le, et vous devriez voir trois images distantes de différents types affichées. En haut, vous pouvez sélectionner une image locale o le fichier de données, et la signature du fichier et/ou le type MIME seront affichés.
Notez que même si une image est renommée, son véritable 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/>
6 votes
I want to perform a client side checking to avoid unnecessary wastage of server resource.
Je ne comprends pas pourquoi vous dites que la validation doit être effectuée du côté du serveur, mais dites ensuite que vous voulez réduire les ressources du serveur. Règle d'or : Ne jamais faire confiance aux données de l'utilisateur . Quel est l'intérêt de vérifier le type MIME du côté client si vous le faites ensuite du côté serveur. Il s'agit certainement d'un "gaspillage inutile de ressources". client ressource" ?10 votes
Fournir une meilleure vérification du type de fichier/un meilleur retour d'information aux utilisateurs côté client est une bonne idée. Cependant, comme vous l'avez indiqué, les navigateurs se basent simplement sur les extensions de fichier pour déterminer la valeur de la propriété
type
propriété pourFile
objets. Le code source de webkit, par exemple, révèle cette vérité. Il est possible d'identifier avec précision les fichiers côté client en recherchant, entre autres, des "octets magiques" dans les fichiers. Je travaille actuellement sur une bibliothèque MIT (dans le peu de temps libre dont je dispose) qui fera exactement cela. Si vous êtes intéressé par mes progrès, jetez un coup d'œil à github.com/rnicholus/determinater .54 votes
@IanClark, le fait est que si le fichier est d'un type non valide, je peux le rejeter du côté client plutôt que de gaspiller la bande passante de téléchargement pour ensuite le rejeter du côté serveur.
0 votes
@RayNicholus, cool mec ! Je le regarderai quand j'aurai le temps. Merci :)
0 votes
Etes-vous sûr que votre fichier de test a toujours le mimetype
image/jpeg
et vous ne l'avez pas modifié en changeant l'extension ?0 votes
Le type Mime n'est pas une solution miracle, c'est juste une hypothèse. Les fichiers binaires eux-mêmes ne portent pas cette propriété, il n'y a donc aucun moyen de l'obtenir de manière transparente côté client. Il est défini par les serveurs lorsqu'ils envoient des données aux clients, mais même dans ce cas, il est souvent deviné par l'extension du fichier, ou explicitement défini par les développeurs backend qui savent quel type de contenu ils envoient. fr.wikipedia.org/wiki/Mime_type
1 votes
@QuestionOverflow Un peu tard, mais j'ai ajouté une solution complète et une démo vivante et fonctionnelle dans ma réponse. Profitez-en.