Puede determinar fácilmente el tipo de archivo MIME con JavaScript FileReader
antes de cargarlo en un servidor. Estoy de acuerdo en que deberíamos preferir la verificación del lado del servidor sobre el lado del cliente, pero la verificación del lado del cliente todavía es posible. Le mostraré cómo y proporcionaré una demostración funcional en la parte inferior.
Verifique que su navegador sea compatible con ambos File
y Blob
. Todos los principales deberían.
if (window.FileReader && window.Blob) {
// All the File APIs are supported.
} else {
// File and Blob are not supported
}
Paso 1:
Puede recuperar la File
información de un <input>
elemento como este ( 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>
Aquí hay una versión de arrastrar y soltar de lo anterior ( 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>
Paso 2:
Ahora podemos inspeccionar los archivos y extraer encabezados y tipos MIME.
✘ Método rápido
Puedes ingenuamente pedirle a Blob el tipo MIME de cualquier archivo que represente usando este patrón:
var blob = files[i]; // See step 1 above
console.log(blob.type);
Para las imágenes, los tipos MIME regresan de la siguiente manera:
image / jpeg
image / png
...
Advertencia: el tipo MIME se detecta desde la extensión del archivo y se puede engañar o suplantar. Se puede cambiar el nombre de a .jpg
a a .png
y el tipo MIME se informará como image/png
.
✓ Método de inspección de encabezado adecuado
Para obtener el tipo MIME de buena fe de un archivo del lado del cliente, podemos ir un paso más allá e inspeccionar los primeros bytes del archivo dado para compararlos con los llamados números mágicos . Tenga en cuenta que no es del todo sencillo porque, por ejemplo, JPEG tiene algunos "números mágicos". Esto se debe a que el formato ha evolucionado desde 1991. Puede salirse con la suya al verificar solo los dos primeros bytes, pero prefiero verificar al menos 4 bytes para reducir los falsos positivos.
Ejemplo de firmas de archivo de JPEG (primeros 4 bytes):
FF D8 FF E0 (SOI + ADD0)
FF D8 FF E1 (SOI + ADD1)
FF D8 FF E2 (SOI + ADD2)
Aquí está el código esencial para recuperar el encabezado del archivo:
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);
Luego puede determinar el tipo MIME real de esta manera (más firmas de archivos 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;
}
Acepte o rechace las cargas de archivos como desee según los tipos MIME esperados.
Manifestación
Aquí hay una demostración funcional para archivos locales y archivos remotos (tuve que omitir CORS solo para esta demostración). Abra el fragmento, ejecútelo, y debería ver tres imágenes remotas de diferentes tipos mostradas. En la parte superior, puede seleccionar una imagen local o archivo de datos, y se mostrará la firma del archivo y / o el tipo MIME.
Tenga en cuenta que incluso si se cambia el nombre de una imagen, se puede determinar su verdadero tipo MIME. Vea abajo.
Captura de pantalla
// 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.
No entiendo por qué dice que la validación debe hacerse en el lado del servidor, pero luego dice que desea reducir los recursos del servidor. Regla de oro: nunca confíes en la entrada del usuario . ¿Cuál es el punto de verificar el tipo MIME en el lado del cliente si lo está haciendo en el lado del servidor? ¿Seguramente eso es un "desperdicio innecesario de recursos del cliente "?