Ви можете легко визначити тип файлу MIME за допомогою JavaScript, FileReader
перш ніж завантажувати його на сервер. Я погоджуюся, що нам слід віддавати перевагу перевірці на стороні сервера над стороною клієнта, але перевірка на стороні клієнта все ще можлива. Я покажу вам, як і вкажіть робочу демонстрацію внизу.
Перевірте, чи підтримує ваш браузер File
і Blob
. Усі основні повинні.
if (window.FileReader && window.Blob) {
// All the File APIs are supported.
} else {
// File and Blob are not supported
}
Крок 1:
Ви можете отримати File
інформацію з такого <input>
елемента ( 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>
Ось версія перетягування вище ( 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>
Крок 2:
Тепер ми можемо перевіряти файли та дражнити заголовки та типи MIME.
✘ Швидкий метод
Ви можете наївно запитати Blob для типу MIME будь-якого файлу, який він представляє, використовуючи цей шаблон:
var blob = files[i]; // See step 1 above
console.log(blob.type);
Для зображень типи MIME повертаються таким чином:
image / jpeg
image / png
...
Caveat: тип MIME виявляється з розширення файлу, і його можна обдурити або підробити. Можна перейменувати а .jpg
на а, .png
а тип MIME буде повідомлено як image/png
.
✓ Правильний метод перевірки заголовка
Щоб отримати тип bonafide MIME на стороні клієнта, ми можемо піти на крок далі і оглянути перші кілька байтів даного файлу для порівняння з т. Зв. магічними числами . Попереджуйте, що це не зовсім просто, оскільки, наприклад, JPEG має кілька "магічних чисел". Це тому, що формат розвивався з 1991 року. Ви можете уникнути перевірки лише перших двох байтів, але я вважаю за краще перевірити щонайменше 4 байти, щоб зменшити помилкові позитиви.
Приклад підписів файлів JPEG (перші 4 байти):
FF D8 FF E0 (SOI + ADD0)
FF D8 FF E1 (SOI + ADD1)
FF D8 FF E2 (SOI + ADD2)
Ось найважливіший код для отримання заголовка файлу:
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);
Потім можна визначити справжній тип MIME, як такий (більше підписів файлів тут і тут ):
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;
}
Прийміть або відхиліть завантаження файлів, як вам подобається, на основі очікуваних типів MIME.
Демо
Ось робоча демонстрація для локальних файлів та віддалених файлів (мені довелося обійти CORS саме для цієї демонстрації). Відкрийте фрагмент, запустіть його, і ви побачите три віддалені зображення різних типів. Угорі ви можете вибрати місцеве зображення або файл даних, і буде показано підпис файлу та / або тип MIME.
Зауважте, що навіть якщо зображення перейменовано, його справжній тип MIME можна визначити. Дивись нижче.
Знімок екрана
// 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.
Я не розумію, чому ви говорите, що перевірку потрібно робити на стороні сервера, але потім ви говорите, що хочете скоротити ресурси сервера. Золоте правило: ніколи не довіряйте користувальницькому вводу . Який сенс перевіряти тип MIME на стороні клієнта, якщо ви просто робите це на стороні сервера. Напевно, це "непотрібне витрачання клієнтського ресурсу"?