如何在上传之前用javascript检查文件的MIME类型? [英] How to check file MIME type with javascript before upload?

查看:126
本文介绍了如何在上传之前用javascript检查文件的MIME类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读似乎表明,文件的MIME类型可以检查使用JavaScript在客户端的问题。现在,我明白,真正的验证仍然需要在服务器端完成。我想执行一个客户端检查,以避免不必要的浪费服务器资源。

为了测试这是否可以在客户端完成,我改变了一个 JPEG 测试文件到 .png 并选择要上传的文件。在发送文件之前,我使用javascript控制台查询文件对象:

  document.getElementsByTagName('input')[0] .files [0]; 

这就是我在Chrome 28.0上得到的结果:


文件{webkitRelativePath:,lastModifiedDate:Tue Oct 16 2012
10:00:00 GMT + 0000(UTC),name:test.png,type: image / png,size:
500055 ...}

显示类型为 image / png 这似乎表明检查是基于文件扩展名而不是MIME类型完成的。我试过Firefox 22.0,它给了我相同的结果。但根据 W3C规范 MIME Sniffing 应该被执行。



我说得对目前没有办法检查与JavaScript的MIME类型?或者我错过了什么?

解决方案

您可以使用JavaScript的 FileReader轻松确定文件MIME类型。 code>在上传到服务器之前。我同意我们应该更喜欢服务器端检查客户端,但客户端检查仍然是可能的。我会告诉你如何在底部提供一个工作演示。






检查您的浏览器是否同时支持 File Blob 。如果(window.FileReader&& window.Blob){
//所有主要应用程序都应该这样做。

 文件API被支持。 
} else {
//不支持File和Blob
}



第1步:

您可以从中检索 File 输入> 元素像这样(
$ p $ < input type =fileid =your-filesmultiple>
< script>
var control = document.getElementById(your-files);
control.addEventListener(change,function(event){
//当控件发生变化时,有新文件
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){
//取消默认动作
event.preventDefault();
var files = event.dataTransfer .files,
for(var i = 0; i< files.length; i ++){
console.log(Filename:+ files [i] .name);
console (Size:+ files [i] .size +bytes);
}
},false);
< / script>






第二步:



我们现在可以检查这些文件,并查找标题和MIME类型。

&#x2718;快速方法

您可以天真地问 Blob 为它所代表的任何文件的MIME类型使用此模式:

  var blob = files [i] ; //见上面的第一步
console.log(blob.type);

对于图片,MIME类型会返回如下所示:


image / jpeg

image / png

...


警告: MIME类型是从文件扩展名中检测到的,可以被愚弄或欺骗。可以将 .jpg 重命名为 .png ,MIME类型将被报告为 image / png






&#x2713;正确的头部检查方法



为了获得客户端文件的真正的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]; //见上面的步骤1 
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);

//根据已知类型

检查文件签名;
fileReader.readAsArrayBuffer(blob);

然后,您可以确定真实的MIME类型(更多文件签名这里这里 $:

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $图像/ PNG;
break;
case47494638:
type =image / gif;
break;
caseffd8ffe0:
caseffd8ffe1:
caseffd8ffe2:
type =image / jpeg;
break;
默认值:
type =unknown; //或者你可以使用blob.type作为后备
break;





根据预期的MIME类型接受或拒绝文件上传。 / b>




演示



这是本地文件的工作演示远程文件(我不得不为这个演示绕过CORS)。打开代码片段,运行它,你会看到三个不同类型的远程图像显示。在顶部,您可以选择一个本地图像数据文件,并显示文件签名和/或MIME类型。



注意即使图像被重命名,也可以确定其真实的MIME类型。请参阅下面的内容。



截图






  //将文件的前几个字节作为十六进制返回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); }回调(url,header); }; fileReader.readAsArrayBuffer(blob);} function getRemoteFileHeader(url,callback){var xhr = new XMLHttpRequest(); //绕过这个demo的CORS  - 淘气,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!'); }; ();}};}};}};};} getBLOBFileHeader(url,blob,headerCallback);}函数printImage(blob){//将此图像添加到文档正文以获得GET成功的证明var fr = new FileReader(); ($()。attr(src,fr.result)).after($(< div>)后的函数fr.onloadend = .text(Blob MIME类型:+ blob.type)); }; fr.readAsDataURL(blob);} //从http://en.wikipedia.org/wiki/List_of_file_signaturesfunction添加更多mimeType(headerString){switch(headerString){case89504e47:type =image / png;打破; case47494638:type =image / gif;打破; caseffd8ffe0:caseffd8ffe1:caseffd8ffe2:type =image / jpeg;打破; default:type =unknown;打破; (返回类型);}函数printHeaderInfo(url,headerString){$(hr)。after($(< div> $(< div>)。text(File header:0x+ headerString)).after($(< div> 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\"];//检查FileReader支持(window.FileReader&& window.Blob){//加载所有的远程图像(var i = 0; i< imageURLsArray.length; i ++){getRemoteFileHeader(imageURLsArray [i],remoteCallback); ('change',function(event){var file = event.target.files [0]; if(file.size> = 2 * 1024 *)处理本地文件* / $(input 1024){alert(文件大小必须至多2MB); return;} remoteCallback(escape(file.name),file);});} else {// File和Blob不被支持$(hr )。($(< div>)。text(看起来你的浏览器不支持FileReader));} / * Drakes,2015 * /   

字体: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>选择图片以查看其文件签名。< / div>< / form>< hr />


I have read this and this questions which seems to suggest that the file MIME type could be checked using javascript on client side. Now, I understand that the real validation still has to be done on server side. I want to perform a client side checking to avoid unnecessary wastage of server resource.

To test whether this can be done on client side, I changed the extension of a JPEG test file to .png and choose the file for upload. Before sending the file, I query the file object using a javascript console:

document.getElementsByTagName('input')[0].files[0];

This is what I get on Chrome 28.0:

File {webkitRelativePath: "", lastModifiedDate: Tue Oct 16 2012 10:00:00 GMT+0000 (UTC), name: "test.png", type: "image/png", size: 500055…}

It shows type to be image/png which seems to indicate that the checking is done based on file extension instead of MIME type. I tried Firefox 22.0 and it gives me the same result. But according to the W3C spec, MIME Sniffing should be implemented.

Am I right to say that there is no way to check the MIME type with javascript at the moment? Or am I missing something?

解决方案

You can easily determine the file MIME type with JavaScript's FileReader before uploading it to a server. I agree that we should prefer server-side checking over client-side, but client-side checking is still possible. I'll show you how and provide a working demo at the bottom.


Check that your browser supports both File and Blob. All major ones should.

if (window.FileReader && window.Blob) {
    // All the File APIs are supported.
} else {
    // File and Blob are not supported
}

Step 1:

You can retrieve the File information from an <input> element like this (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>

Here is a drag-and-drop version of the above (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>


Step 2:

We can now inspect the files and tease out headers and MIME types.

✘ Quick method

You can naïvely ask Blob for the MIME type of whatever file it represents using this pattern:

var blob = files[i]; // See step 1 above
console.log(blob.type);

For images, MIME types come back like the following:

image/jpeg
image/png
...

Caveat: The MIME type is detected from the file extension and can be fooled or spoofed. One can rename a .jpg to a .png and the MIME type will be be reported as image/png.


✓ Proper header-inspecting method

To get the bonafide MIME type of a client-side file we can go a step further and inspect the first few bytes of the given file to compare against so-called magic numbers. Be warned that it's not entirely straightforward because, for instance, JPEG has a few "magic numbers". This is because the format has evolved since 1991. You might get away with checking only the first two bytes, but I prefer checking at least 4 bytes to reduce false positives.

Example file signatures of JPEG (first 4 bytes):

FF D8 FF E0 (SOI + ADD0)
FF D8 FF E1 (SOI + ADD1)
FF D8 FF E2 (SOI + ADD2)

Here is the essential code to retrieve the file header:

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);

You can then determine the real MIME type like so (more file signatures here and here):

switch (header) {
    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"; // Or you can use the blob.type as fallback
        break;
}

Accept or reject file uploads as you like based on the MIME types expected.


Demo

Here is a working demo for local files and remote files (I had to bypass CORS just for this demo). Open the snippet, run it, and you should see three remote images of different types displayed. At the top you can select a local image or data file, and the file signature and/or MIME type will be displayed.

Notice that even if an image is renamed, its true MIME type can be determined. See below.

Screenshot


// 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/>

这篇关于如何在上传之前用javascript检查文件的MIME类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆