HTML5 file api 读取文件MD5码

背景

自从html5 file api出现以来,我们可以做的事情越来越多,越来越有趣了,之前介绍过 《HTML5实现断点续传》、《HTML5实现拖拽下载》等关于File API的一些实例,今天和大家分享一下,如何用HTML5 file api读取文件的MD5码。

MD5码在文件的唯一性识别上有很重要的应用,业内常用MD5进行文件识别、文件秒传、文件安全性检查等。

废话不多说,直接说重点。

实现

首先监听文本框的变化,告诉浏览器,你就看着这个input 如果有变化你就立刻执行之后的东西。

document.getElementById("file").addEventListener("change", function() {
    //...
}

读取文件很容易通过input的files对象就可以读取到用户所选择的文件

file = document.getElementById("file").files[0]

之后就是分割文件了,由于文件可能会很大,比如说10G,20G所以一次性把它交个内存来处理会显得很残忍。于是我们把文件以2M作为一个片段进行分割,算出总共需要分成多少片

var chunkSize = 2097152;
chunks = Math.ceil(file.size / chunkSize);

接着分割文件

//file的slice方法,注意它的兼容性,在不同浏览器的写法不同
blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice
//然后指定file和开始结束的片段,就可以得到切割的文件了。
blobSlice.call(file, start, end)

一切做好之后,就要去读取文件的具体信息了。

这里我们用了Javascript的 FileReader() 方法,这个方法可以去读取用户本地文件的详细内容。

他的用法如下

var fileReader = new FileReader();
//blobSlice.call(file, start, end)就是之前分割文件的方法。
//这里只要维护好start和end,就能一片一片的把文件传给fileRader对象了
fileReader.readAsBinaryString(blobSlice.call(file, start, end));
//最终每一段文件处理完毕都会触发fileReader的onload事件
fileReader.onload = function(e){
    //e.target.result 就是我们要的片段信息
    //这里缓存获取到的片段,当所有片段完毕之后就可以进行MD5的计算了。
}

好了,到这里万事俱备只欠东风了。 由于原生的Javascript没有直接计算MD5的方法,这里我们引用了一个比较好的spark-md5库来辅助我们进行MD5的计算。 比较好的一点是,spark-md5处理文件的话也可以按片来计算。

spark = new SparkMD5();
spark.appendBinary(filepice1);
spark.appendBinary(filepice2);
spark.appendBinary(filepice3);
....
//所有的分片处理好之后调用下面的方法就能获取到文件的MD5了
spark.end()

至此整个过程都已经结束了。 简单的描述就是:利用input选择文件 -> 对文件进行分片 -> 用FileReader方法读取文件 -> 交由Spark-md5进行处理。

代码

DEMO传送门

附上完成的代码:

//注意此方法引用了SparkMD5库 library:https://github.com/satazor/SparkMD5
//监听文本框变化
document.getElementById("file").addEventListener("change", function() {
    //声明必要的变量
    var fileReader = new FileReader(), box = document.getElementById('box');
    //文件分割方法(注意兼容性)
    blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice, 
    file = document.getElementById("file").files[0], 

    //文件每块分割2M,计算分割详情
    chunkSize = 2097152,                
    chunks = Math.ceil(file.size / chunkSize), 
    currentChunk = 0, 

    //创建md5对象(基于SparkMD5)
    spark = new SparkMD5();

    //每块文件读取完毕之后的处理
    fileReader.onload = function(e) {
        console.log("读取文件", currentChunk + 1, "/", chunks);
        //每块交由sparkMD5进行计算
        spark.appendBinary(e.target.result);
        currentChunk++;

        //如果文件处理完成计算MD5,如果还有分片继续处理
        if (currentChunk < chunks) {
            loadNext();
        } else {
            console.log("finished loading");
            box.innerText = 'MD5 hash:' + spark.end();
            console.info("计算的Hash", spark.end());
        }
    };

     //处理单片文件的上传
     function loadNext() {
         var start = currentChunk * chunkSize, end = start + chunkSize >= file.size ? file.size : start + chunkSize;

         fileReader.readAsBinaryString(blobSlice.call(file, start, end));
     }

      loadNext();
});

DEMO传送门

Write a response...
Mofei Zhu
publish
Mofei
2017-02-24 12:05
@image72  
ok,明白你的意思了 document.querySelector 选择器确实比按ID检索要方便
0
 Replay
@Mofei  
Replay
image72
2017-02-24 11:12
@Mofei   document.querySelector 更便捷的写法
0
 Replay
@image72  
Replay
Mofei
2017-01-14 11:01
@image72  这是在玩注入么? 哈哈
0
 Replay
@Mofei  
Replay
image72
2016-11-20 01:26
var $el = document.querySelector.bind(document);
var elemBox = $el('#box');
0
 Replay
@image72  
Replay
image72
2016-11-20 01:26
var $el = document.querySelector.bind(document);
var elemBox = $el('#box');
0
 Replay
@image72  
Replay
Mofei
2016-03-07 14:22
@luciferkkk Demo中用的是这个库进行计算的 https://github.com/satazor/js-spark-md5 ,你可以看一下
0
 Replay
@Mofei  
Replay
luciferkkk
2016-03-07 14:13
@Mofei 理论上MD5算法应该是一样的啊
0
 Replay
@luciferkkk  
Replay
Mofei
2016-03-07 13:12
@luciferkkk 散列的实现有各种方法,不同的实现算出来的值不一致
0
 Replay
@Mofei  
Replay
luciferkkk
2016-03-07 12:32
和系统里md5sum计算出来的值不一样?
0
 Replay
@luciferkkk  
Replay
Mofei
2015-11-03 10:09
@一瓶小酱油 大文件确实会很慢,有没有尝试通过文件特征去判断呢?比如说“文件名+修改时间+文件大小” ,先初步判断一下,如果命中的话,说明可能有缓存,当然了这也只是个治标不治本的方案,后续如果有发现更好的方案,第一时间告诉你。
0
 Replay
@Mofei  
Replay
一瓶小酱油
2015-10-26 09:49
先感谢一下博主的分享,我已经用上了。不过发现一个问题,就是计算大文件的MD5实在是太慢,3G大概在2分钟左右。别说2分钟,5秒钟之外是完全行不通的,那样客户体验实在太差。我现在正在寻找解决方案,不知博主有没有解决方案呢?
0
 Replay
@一瓶小酱油  
Replay
匿名
2015-06-08 18:18
我直接把你的代码放到我的上传图片代码里面 为什么会报错‘TypeError: 'slice' called on an object that does not implement interface Blob.’
0
 Replay
@  
Replay
Mofei
2015-05-21 16:24
@怎么点击DEMO那里测试不显示md5? 新特性需要支持h5的浏览器,推荐使用chrome试试
0
 Replay
@Mofei  
Replay
怎么点击DEMO那里测试不显示md5?
2015-05-21 16:01
@Mofei
0
 Replay
@怎么点击DEMO那里测试不显示md5?  
Replay
Mofei
2015-05-20 12:04
@Mofei的好朋友 感谢支持~
0
 Replay
@Mofei  
Replay
匿名
2015-05-20 12:03
亲测,很有效
0
 Replay
@  
Replay
表演达人
2015-05-14 12:45
文件hash 超强版:http://www.atool.org/file_hash.php
0
 Replay
@表演达人  
Replay
huang007guo
2014-09-04 15:07
readAsBinaryString 可能是过时的 如果没有(readAsBinaryString )可以使用 readAsArrayBuffer更具备兼容性(如ie10等) function onfilechange(evt) { var reader = new FileReader(); reader.onload = function(evt) { var chars = new Uint8Array(evt.target.result); var CHUNK_SIZE = 0x8000; var index = 0; var length = chars.length; var result = ''; var slice; while (index < length) { slice = chars.subarray(index, Math.min(index + CHUNK_SIZE, length)); result += String.fromCharCode.apply(null, slice); index += CHUNK_SIZE; } // Here you have file content as Binary String in result var }; reader.readAsArrayBuffer(evt.target.files[0]); } from:http://stackoverflow.com/questions/24057133/javascript-readasarraybuffer-returns-empty-array-buffer
0
 Replay
@huang007guo  
Replay