文件类型很多,我们常用的方式是通过后缀来辨识文件类型,那么如果文件真实类型与后缀不一致呢,我们怎么才能判别真实的文件类型,识别哪些伪造的文件。我们一个个来说:
一、通过后缀
先获取上传文件的文件名,然后根据文件名截取文件后缀,代码如下:<!DOCTYPE html> <html> <head> </head> <body> <input type="file" id="fileInput"> <div id="output"></div> <script type="text/javascript"> fileInput.onchange = function() { var filename=fileInput.files[0].name; var suffixname =filename.substr(filename.lastIndexOf(".")).toLowerCase(); output.innerHTML = suffixname; }; </script> </body> </html>
二、通过file.type
通过file对象的type属性既可以获取上传文件的类型,类似于image/png这种格式的,代码如下:<!DOCTYPE html> <html> <head> </head> <body> <input type="file" id="fileInput"> <div id="output"></div> <script type="text/javascript"> fileInput.onchange = function() { var filetype=fileInput.files[0].type; output.innerHTML = filetype; }; </script> </body> </html>
以上两种办法获取上传文件的类型存在一个问题,如果我把别的类型的后缀更换了,那么以上两种办法获取的文件类型无法获取真实的文件类型,例如我把example.doc文件改成了example.png,那么上传后,上面两种方法获取文件类型分别是.png和image/png,但是其实这个文件是doc文档类型的,那么js如何获取文件的真实类型呢,下面我们说第三种,通过二进制读取文件头部字节的方式。
三、通过二进制获取mine type
我们可以通过读取文件的头4个字节结合pattern与mask进行文件签名匹配就可以获取到该文件的真实类型,代码如下:<!DOCTYPE html> <html> <head> </head> <body> <input type="file" id="fileInput"> <div id="output"></div> <script type="text/javascript"> function loadMime(file, callback) { //已知文件mine类型标识 var mimes = [ { mime: 'image/jpeg', pattern: [0xFF, 0xD8, 0xFF], mask: [0xFF, 0xFF, 0xFF], }, { mime: 'image/png', pattern: [0x89, 0x50, 0x4E, 0x47], mask: [0xFF, 0xFF, 0xFF, 0xFF], } // 更多扩展的mine标识请看这个 https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern ]; function check(bytes, mime) { for (var i = 0, l = mime.mask.length; i < l; ++i) { if ((bytes[i] & mime.mask[i]) - mime.pattern[i] !== 0) { return false; } } return true; } var blob = file.slice(0, 4); //读取文件的头部4个字节 var reader = new FileReader(); reader.onloadend = function(e) { if (e.target.readyState === FileReader.DONE) { var bytes = new Uint8Array(e.target.result); for (var i=0, l = mimes.length; i<l; ++i) { if (check(bytes, mimes[i])) return callback("Mime: " + mimes[i].mime + " <br> Browser:" + file.type); } return callback("Mime: unknown <br> Browser:" + file.type); } }; reader.readAsArrayBuffer(blob); } fileInput.onchange = function() { loadMime(fileInput.files[0], function(mime) { output.innerHTML = mime; }); }; </script> </body> </html>每个类型的文件签名都不一样,如图:
更多文件签名头部pattern与mask请查找这篇文章:https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
还有一种也是直接通过读取文件签名来匹配的,他还能通过url读取远程图片的真实类型,代码如下:
<!DOCTYPE html> <html> <head> <style> img { max-height: 200px } div { height: 26px; font: Arial; font-size: 12pt } form { height: 40px; } </style> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/jquery-3.2.1.min.js"></script> </head> <body> <form> <input type="file" /> <div>Choose an image to see its file signature.</div> </form> <hr/> <script type="text/javascript"> // 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', "/asset/logo.png"); 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 = [""]; // 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 */ </script> </body> </html>
网友评论0