怎么给文件生成MD5

简介: 怎么给文件生成MD5

怎么给文件生成MD5


MD5 的核心是通过算法把任意长度的原始数据映射成128 bit 的数据。把一串数据经过处理,得到另一个固定长度的数据。是一种Hash算法,全称为 消息摘要算法版本5(Message Digest Algorithm 5)。

不同原始数据会有不同的 MD5 值。 所以不同的文件MD5的值也不一样。

一般在上传文件的场景里,会根据MD5实现续传、秒传的功能。

本文简单写下怎么生成 MD5,这里用到插件spark-md5

TL;DR

  • 怕麻烦,10M以内生成MD5,直接用法一就行
  • 怕麻烦,30M以内生成MD5,直接用法二就行
  • 再大一点,用法三吧
  • 但法三可以用在以上所有场景

法一:生成文件的 MD5

生成文件的 MD5,简单思路如下:

  • 创建 FileReader 实例,读文件
  • 读完之后,成功状态下,直接使用SparkMD5.hashBinary

网络异常,图片无法展示
|

具体代码在文末。

缺陷

这种方式,当文件越大的时候,生成 md5 的速度也就越慢,比如 40M 的文件可能需要 1s 才生成 md5。当页面还有其他的交互的时候,将会堵塞其他交互,导致页面假死状态。

举个例子:加个按钮,写个点击事件。选择文件之后,立即点击按钮,会发现,当文件越大的时候,弹框的速度越来越迟钝。

<input id="upload" type="file" onchange="selectLocalFile" />
<!-- 这里加个按钮,选择文件完之后 -->
<button onclick="alert(1)">测试线程堵塞</button>
<script>
  upload.onchange = async (e) => {
    const file = e.target.files[0];
    console.time("timeCreateMd5");
    const md5 = await createFileMd5(file);
    console.log(file.size);
    //  会打印timeCreateMd5: 959.31396484375 ms
    console.timeEnd("timeCreateMd5");
  };
</script>

法二:在worker中生成md5

所以我们使用 web-workerworker 线程计算 hash,这样用户仍可以在主界面正常的交互,不会引起堵塞。

当前页面的修改,如下:

网络异常,图片无法展示
|

新增在worker里执行的hash.js文件如下:

self.importScripts("https://unpkg.com/spark-md5@3.0.1/spark-md5.min.js");
// 生成文件 hash
self.onmessage = async e => {
  const file = e.data
  const md5 = await createFileMd5(file)
  self.postMessage(md5);
};
function createFileMd5(file){
  // ...
  // 同之前的,但以下需要修改,增加self的前缀
  isSuccess
        ? resolve(self.SparkMD5.hashBinary(result))
        : reject(new Error("读取出错了"));
}

到这,其实虽然生成大文件的md5耗费时间长,但起码不会堵塞页面主线程了。

也有缺陷

可以看到计算md5是将整个文件读完才看到,这样当文件过大是极其耗内存的。所以需要分片读取生成md5

法三:将文件分片生成md5

仔细看spark-md5,其实作者也是推荐分片读取,类似nodejs里面的流一样,这样不需要占据大量内存。

先将文件,按照一定大小分块chunk,这边直接使用File.slice了。

然后将chunks传给另一个线程计算md5,这边大文件可能需要进度条,所以有一个进度,按需求使用。

网络异常,图片无法展示
|
网络异常,图片无法展示
|

附注:代码

代码:生成小文件的 MD5

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input id="upload" type="file" onchange="selectLocalFile" />
    <script src="https://unpkg.com/spark-md5@3.0.1/spark-md5.min.js"></script>
    <script>
      const upload = document.querySelector("#upload");
      upload.onchange = async (e) => {
        const file = e.target.files[0];
        const md5 = await createFileMd5(file);
        console.log(md5);
      };
      function createFileMd5(file) {
        return new Promise((resolve, reject) => {
          // 创建FileReader实例
          const fileReader = new FileReader();
          // 开始读文件
          fileReader.readAsBinaryString(file);
          // 文件读完之后,触发load事件
          fileReader.onload = (e) => {
            // e.target就是fileReader实例
            console.log(e.target);
            // result是fileReader读到的部分
            const result = e.target.result;
            // 如果读到的长度和文件长度一致,则读取成功
            const isSuccess = file.size === result.length;
            // 读取成功,则生成MD5,扔出去。失败就报错
            isSuccess
              ? resolve(SparkMD5.hashBinary(result))
              : reject(new Error("读取出错了"));
          };
          //   读取过程中出错也直接报错
          fileReader.onerror = () => reject(new Error("读取出错了"));
        });
      }
    </script>
  </body>
</html>

代码:在worker中生成md5

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input id="upload" type="file" onchange="selectLocalFile" />
    <button onclick="alert(1)">测试线程堵塞</button>
    <script>
      const upload = document.querySelector("#upload");
      upload.onchange = async (e) => {
        const file = e.target.files[0];
        console.time("timeCreateMd5");
        const md5 = await createFileMd5InWorker(file);
        console.log(file.size);
        console.timeEnd("timeCreateMd5");
      };
      // 生成文件 md5(web-worker)
      function createFileMd5InWorker(file) {
        return new Promise((resolve) => {
          // 新建worker线程,执行hash.js
          const worker = new Worker("./hash.js");
          // 给线程传file
          worker.postMessage(file);
          // 当线程传消息的时候,接受消息
          worker.onmessage = (e) => {
            const md5 = e.data;
            md5 && resolve(md5)
          };
        });
      }
    </script>
  </body>
</html>
// hash.js
self.importScripts("https://unpkg.com/spark-md5@3.0.1/spark-md5.min.js");
// 生成文件 md5
self.onmessage = async e => {
  const file = e.data
  const md5 = await createFileMd5(file)
  self.postMessage(md5);
  self.close()
};
function createFileMd5(file) {
  return new Promise((resolve, reject) => {
    // 创建FileReader实例
    const fileReader = new FileReader();
    // 开始读文件
    fileReader.readAsBinaryString(file);
    // 文件读完之后,触发load事件
    fileReader.onload = (e) => {
      // e.target就是fileReader实例,这里用this也是指fileReader实例
      console.log(e.target);
      // result是fileReader读到的部分
      const result = e.target.result;
      // 如果读到的长度和文件长度一致,则读取成功
      const isSuccess = file.size === result.length;
      // 读取成功,则生成MD5,扔出去。失败就报错
      isSuccess
        ? resolve(self.SparkMD5.hashBinary(result))
        : reject(new Error("读取出错了"));
    };
    //   读取过程中出错也直接报错
    fileReader.onerror = () => reject(new Error("读取出错了"));
  });
}

代码:分片读取生成md5

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input id="upload" type="file" onchange="selectLocalFile" />
    <button onclick="alert(1)">测试线程堵塞</button>
    <script>
      const upload = document.querySelector("#upload");
      upload.onchange = async (e) => {
        const file = e.target.files[0];
        const chunks = createFileChunk(file)
        console.time("timeCreateMd5");
        // 这里注意放chunks
        const {md5} = await createFileMd5InWorker(chunks);
        console.log(file.size);
        console.timeEnd("timeCreateMd5");
      };
      // 生成文件切片
      function createFileChunk(file, size = 4 * 1024 * 1024) {
        let chunks = [];
        let cur = 0;
        while (cur < file.size) {
          chunks.push(file.slice(cur, cur + size));
          cur += size;
        }
        return chunks;
      }
      // 生成文件 hash(web-worker)
      function createFileMd5InWorker(fileChunks) {
        return new Promise((resolve) => {
          const worker = new Worker("./hash.js");
          worker.postMessage({ fileChunks });
          worker.onmessage = (e) => {
            // 这边加了进度条 这里的进度条,看需要显示
            const { percentage, hash } = e.data;
            console.log(percentage)
            // 计算出hash之后,扔出去
            hash &&resolve(hash);
          };
        });
      }
    </script>
  </body>
</html>
// 直接copy的  https://juejin.cn/post/6844904046436843527#heading-17
self.importScripts("./js/lib/spark-md5.min.js"); // 导入脚本
// 生成文件 hash
self.onmessage = e => {
  const { fileChunks } = e.data;
  console.log(fileChunks)
//   const { fileChunks } = e.data;
  const spark = new self.SparkMD5.ArrayBuffer();
  let percentage = 0;
  let count = 0;
  const loadNext = index => {
    const reader = new FileReader();
    reader.readAsArrayBuffer(fileChunks[index]);
    reader.onload = e => {
      count++;
      spark.append(e.target.result);
      if (count === fileChunks.length) {
        self.postMessage({
          percentage: 100,
          hash: spark.end()
        });
        self.close();
      } else {
        percentage += 100 / fileChunks.length;
        self.postMessage({
          percentage
        });
        loadNext(count);
      }
    };
  };
  loadNext(0);
};

引用

目录
相关文章
|
JavaScript CDN
js:spark-md5分片计算文件的md5值
js:spark-md5分片计算文件的md5值
1811 0
|
Web App开发 域名解析 缓存
如何在 Ubuntu 20.04 上安装 Node.js 和 npm
本文我们主要为大家介绍在 Ubuntu 20.04 上安装 Node.js 和 npm 的三种不同的方式。
161632 7
如何在 Ubuntu 20.04 上安装 Node.js 和 npm
|
机器学习/深度学习 Windows
解决R语言出现“二进列运算符中有非数值参数”的错误!
解决R语言出现“二进列运算符中有非数值参数”的错误!
2662 0
解决R语言出现“二进列运算符中有非数值参数”的错误!
|
9月前
|
人工智能 移动开发 前端开发
WeaveFox:蚂蚁集团推出 AI 前端智能研发平台,能够根据设计图直接生成源代码,支持多种客户端和技术栈
蚂蚁团队推出的AI前端研发平台WeaveFox,能够根据设计图直接生成前端源代码,支持多种应用类型和技术栈,提升开发效率和质量。本文将详细介绍WeaveFox的功能、技术原理及应用场景。
5565 68
WeaveFox:蚂蚁集团推出 AI 前端智能研发平台,能够根据设计图直接生成源代码,支持多种客户端和技术栈
|
存储 算法 安全
使用MD5当做文件的唯一标识,这样安全么?
MD5常用于文件完整性验证。通过对文件进行MD5哈希计算,可以生成唯一的哈希值,用于识别文件的内容是否发生改变。这在文件传输和数据备份中特别有用。 MD5也经常被用于密码存储,将用户密码经过MD5哈希后存储,而不是直接保存明文密码,以增加安全性。
2918 0
使用MD5当做文件的唯一标识,这样安全么?
|
10月前
|
人工智能 自然语言处理 物联网
llama factory 从数据集起步 跑通 qwen系列开源生成式大模型 微调
`dataset_info.json` 文件用于管理 llama factory 中的所有数据集,支持 `alpaca` 和 `sharegpt` 格式。通过配置此文件,可以轻松添加自定义数据集。数据集的相关参数包括数据源地址、数据集格式、样本数量等,支持 Hugging Face 和 ModelScope 两个平台的数据集仓库。针对不同格式的数据集,提供了详细的配置示例,如 `alpaca` 格式的指令监督微调数据集、偏好数据集等,以及 `sharegpt` 格式的多模态数据集等。今天我们通过自定义数据集的方式来进行qwen2.5_14B_instruct模型进行微调
3843 7
|
11月前
|
安全 Shell PHP
BUUCTF-WEB(第二天)
BUUCTF-WEB(第二天)
160 2
|
安全 定位技术 网络安全
安全策略中的访问策略
【8月更文挑战第11天】
310 3
|
12月前
|
网络协议 Linux iOS开发
8-18|如何查看本地端口
8-18|如何查看本地端口
|
Ubuntu 安全 网络协议