È possibile determinare facilmente il tipo MIME di file con JavaScript FileReader
prima di caricarlo su un server. Sono d'accordo che dovremmo preferire il controllo sul lato server sul lato client, ma il controllo sul lato client è ancora possibile. Ti mostrerò come e fornirò una demo funzionante in fondo.
Verifica che il tuo browser supporti sia File
e Blob
. Tutti i principali dovrebbero.
if (window.FileReader && window.Blob) {
// All the File APIs are supported.
} else {
// File and Blob are not supported
}
Passo 1:
È possibile recuperare le File
informazioni da un <input>
elemento come questo ( rif ):
<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>
Ecco una versione drag-and-drop di quanto sopra ( 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>
Passo 2:
Ora possiamo ispezionare i file e prendere in giro intestazioni e tipi MIME.
✘ Metodo rapido
Puoi ingenuamente chiedere a Blob il tipo MIME di qualunque file rappresenti usando questo modello:
var blob = files[i]; // See step 1 above
console.log(blob.type);
Per le immagini, i tipi MIME tornano come segue:
image / jpeg
image / png
...
Avvertenza: il tipo MIME viene rilevato dall'estensione del file e può essere ingannato o falsificato. Si può rinominare a .jpg
in a .png
e il tipo MIME verrà segnalato come image/png
.
✓ Metodo di ispezione delle intestazioni corretto
Per ottenere il tipo MIME in buona fede di un file sul lato client possiamo fare un passo ulteriore e ispezionare i primi pochi byte del file dato per confrontare i cosiddetti numeri magici . Tieni presente che non è del tutto semplice perché, ad esempio, JPEG ha alcuni "numeri magici". Questo perché il formato si è evoluto dal 1991. Potresti cavartela controllando solo i primi due byte, ma preferisco controllare almeno 4 byte per ridurre i falsi positivi.
Firme di file di esempio di JPEG (primi 4 byte):
FF D8 FF E0 (SOI + ADD0)
FF D8 FF E1 (SOI + ADD1)
FF D8 FF E2 (SOI + ADD2)
Ecco il codice essenziale per recuperare l'intestazione del file:
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);
È quindi possibile determinare il tipo MIME reale in questo modo (più firme di file qui e qui ):
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;
}
Accetta o rifiuta i caricamenti di file come preferisci in base ai tipi MIME previsti.
dimostrazione
Ecco una demo funzionante per file locali e file remoti (ho dovuto bypassare CORS solo per questa demo). Apri lo snippet, eseguilo e dovresti visualizzare tre immagini remote di diverso tipo visualizzate. Nella parte superiore è possibile selezionare un'immagine o un file di dati locali e verranno visualizzati la firma del file e / o il tipo MIME.
Si noti che anche se un'immagine viene rinominata, è possibile determinare il suo vero tipo MIME. Vedi sotto.
Immagine dello schermo
// 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/>
I want to perform a client side checking to avoid unnecessary wastage of server resource.
Non capisco come mai tu dica che la convalida deve essere fatta sul lato server, ma poi dici che vuoi ridurre le risorse del server. Regola d'oro: non fidarti mai dell'input dell'utente . Qual è il punto di controllare il tipo MIME sul lato client se lo stai facendo solo sul lato server. Sicuramente si tratta di uno "spreco non necessario di risorse client "?