Golang 获取文件md5校验的方法以及效率对比


Posted in Golang onMay 08, 2021

近期有一个需求:获取多个文件 md5 校验和判断是否存在重复文件,因为文件数量较多,有的文件还比较大,需要处理的文件还没有到位,我就考虑了一下效率的问题。

目前我已知的 Golang 中获取 md5 校验和的方法有两个

这里直接给出实现源码。

package main
import (
 "crypto/md5"
 "flag"
 "fmt"
 "io"
 "io/ioutil"
 "os"
)
var which = flag.Bool("which", true, "")
var path = flag.String("path", "", "")
var cnt = flag.Int("cnt", 100, "")
func aaa() {
 f, err := os.Open(*path)
 if err != nil {
  fmt.Println("Open", err)
  return
 }
 defer f.Close()
 body, err := ioutil.ReadAll(f)
 if err != nil {
  fmt.Println("ReadAll", err)
  return
 }
 md5.Sum(body)
 //fmt.Printf("%x\n", md5.Sum(body))
}
func bbb() {
 f, err := os.Open(*path)
 if err != nil {
  fmt.Println("Open", err)
  return
 }
 defer f.Close()
 md5hash := md5.New()
 if _, err := io.Copy(md5hash, f); err != nil {
  fmt.Println("Copy", err)
  return
 }
 md5hash.Sum(nil)
 //fmt.Printf("%x\n", md5hash.Sum(nil))
}
func main() {
 flag.Parse()
 for i := 0; i < *cnt; i++ {
  if *which {
   aaa()
  } else {
   bbb()
  }
 }
}

还有可供参考的获取 md5 校验和的 Shell 命令

md5 -- calculate a message-digest fingerprint (checksum) for a file
md5 [-pqrtx] [-s string] [file ...]

测试文件是公司项目的日志文件

banjakukutekiiMac:shell panshiqu$ ls -an | grep by
-rw-r--r--   1 501  20   7285957 11 17 16:14 by.out
banjakukutekiiMac:shell panshiqu$ cp by.out by2.out
banjakukutekiiMac:shell panshiqu$ cat by.out >> by2.out
banjakukutekiiMac:shell panshiqu$ ls -an | grep by
-rw-r--r--   1 501  20   7285957 11 17 16:14 by.out
-rw-r--r--   1 501  20  14571914 11 17 17:03 by2.out

下面效率展示

banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=true -path="by.out"
real 0m0.027s
user 0m0.017s
sys 0m0.012s
banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=true -path="by2.out"
real 0m0.048s
user 0m0.033s
sys 0m0.018s
banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=false -path="by.out"
real 0m0.018s
user 0m0.012s
sys 0m0.004s
banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=false -path="by2.out"
real 0m0.031s
user 0m0.024s
sys 0m0.005s
banjakukutekiiMac:shell panshiqu$ time md5 by.out
MD5 (by.out) = 9d79e19a00cef1ae1bb6518ca4adf9de
real 0m0.023s
user 0m0.019s
sys 0m0.006s
banjakukutekiiMac:shell panshiqu$ time md5 by2.out
MD5 (by2.out) = 0a029a460a20e8dcb00d032d6fab74c6
real 0m0.042s
user 0m0.037s
sys 0m0.009s

总结:

不管什么方法都会随着文件变大时间会变长,上面的例子大约都是2倍

io.Copy 方法效率最高,建议大家这样使用

补充:Go语言:md5计算方法的效率研究

研究了一下Go的md5计算方法,目前来看,效率最高运行最快的写法是调用md5.Sum()函数返回16字节checksum,然后把每个字节的高4位和低4位分别映射成16进制字符存到两个字节里,得到32字节,再转成字符串。

FastMD5较其它算法效率提高了至少46%以上。

const hextable = "0123456789abcdef" 
//作者: pengpengzhou
func FastMD5(str string) string {
	src := md5.Sum([]byte(str))
	var dst = make([]byte, 32)
	j := 0
	for _, v := range src {
		dst[j] = hextable[v>>4]
		dst[j+1] = hextable[v&0x0f]
		j += 2
	}
	return string(dst)
}

Go Test Benchmark测试结果:

goos: linux
goarch: amd64
pkg: example
BenchmarkFastMD5-4       5564898               205 ns/op
BenchmarkV1-4            3461698               379 ns/op
BenchmarkV2-4            2277235               516 ns/op
BenchmarkV3-4            2158122               527 ns/op
PASS
ok      example 6.440s

详细代码如下:

package main 
import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
)
 
const hextable = "0123456789abcdef"
 
func FastMD5(str string) string {
	src := md5.Sum([]byte(str))
	var dst = make([]byte, 32)
	j := 0
	for _, v := range src {
		dst[j] = hextable[v>>4]
		dst[j+1] = hextable[v&0x0f]
		j += 2
	}
	return string(dst)
}
 
func md5V1(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}
 
func md5V2(str string) string {
	data := []byte(str)
	has := md5.Sum(data)
	md5str := fmt.Sprintf("%x", has)
	return md5str
}
 
func md5V3(str string) string {
	w := md5.New()
	io.WriteString(w, str)
	md5str := fmt.Sprintf("%x", w.Sum(nil))
	return md5str
}
 
func main() {
	str := "中文"
	fmt.Println(FastMD5(str))
	fmt.Println(md5V1(str))
	fmt.Println(md5V2(str))
	fmt.Println(md5V3(str))
}
package main 
import (
	"testing"
)
 
var str = "golang中文教程"
 
func BenchmarkFastMD5(b *testing.B) {
	for i := 0; i < b.N; i++ {
		FastMD5(str)
	}
}
 
func BenchmarkV1(b *testing.B) {
	for i := 0; i < b.N; i++ {
		md5V1(str)
	}
}
 
func BenchmarkV2(b *testing.B) {
	for i := 0; i < b.N; i++ {
		md5V2(str)
	}
}
 
func BenchmarkV3(b *testing.B) {
	for i := 0; i < b.N; i++ {
		md5V3(str)
	}
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
golang如何去除多余空白字符(含制表符)
Apr 25 Golang
golang 实现对Map进行键值自定义排序
Apr 28 Golang
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 Golang
Golang 如何实现函数的任意类型传参
Apr 29 Golang
使用golang编写一个并发工作队列
May 08 Golang
Go语言实现Base64、Base58编码与解码
Jul 26 Golang
Golang表示枚举类型的详细讲解
Sep 04 Golang
golang中的struct操作
Nov 11 Golang
golang操作rocketmq的示例代码
Apr 06 Golang
golang定时器
Apr 14 Golang
Golang 链表的学习和使用
Apr 19 Golang
Go Grpc Gateway兼容HTTP协议文档自动生成网关
Jun 16 Golang
GoLang中生成UUID唯一标识的实现
May 08 #Golang
聊聊golang中多个defer的执行顺序
May 08 #Golang
Golang全局变量加锁的问题解决
golang 实现并发求和
May 08 #Golang
golang中的并发和并行
May 08 #Golang
关于golang高并发的实现与注意事项说明
May 08 #Golang
基于Golang 高并发问题的解决方案
May 08 #Golang
You might like
PHP中用正则表达式清除字符串的空白
2011/01/17 PHP
php实现最简单的MVC框架实例教程
2014/09/08 PHP
PHP检测接口Traversable用法详解
2017/12/29 PHP
php连接MSsql server的五种方法总结
2018/03/04 PHP
PHP strripos函数用法总结
2019/02/11 PHP
JavaScript高级程序设计 阅读笔记(七) ECMAScript中的语句
2012/02/27 Javascript
不用锚点也可以平滑滚动到页面的指定位置实现代码
2013/05/08 Javascript
JavaScript将相对地址转换为绝对地址示例代码
2013/07/19 Javascript
ExtJS自定义主题(theme)样式详解
2013/11/18 Javascript
JS对象与json字符串格式转换实例
2014/10/28 Javascript
JavaScript的this关键字的理解
2016/06/18 Javascript
在javascript中使用com组件的简单实现方法
2016/08/17 Javascript
HTML中setCapture、releaseCapture 使用方法浅析
2016/09/25 Javascript
原生js实现网易轮播图效果
2020/04/10 Javascript
js实现瀑布流效果(自动生成新的内容)
2017/03/16 Javascript
nodejs入门教程五:连接数据库的方法分析
2017/04/24 NodeJs
jQuery Layer弹出层传值到父页面的实现代码
2017/08/17 jQuery
使用vux实现上拉刷新功能遇到的坑
2018/02/08 Javascript
使用layui实现的左侧菜单栏以及动态操作tab项方法
2019/09/10 Javascript
利用layer实现表单完美验证的方法
2019/09/26 Javascript
vue 计算属性和侦听器的使用小结
2021/01/25 Vue.js
Python实现爬取逐浪小说的方法
2015/07/07 Python
Ubuntu下创建虚拟独立的Python环境全过程
2017/02/10 Python
Python使用PyCrypto实现AES加密功能示例
2017/05/22 Python
Python中的并发处理之asyncio包使用的详解
2018/04/03 Python
python基础梳理(一)(推荐)
2019/04/06 Python
pyqt实现.ui文件批量转换为对应.py文件脚本
2019/06/19 Python
Django学习之文件上传与下载
2019/10/06 Python
30秒学会30个超实用Python代码片段【收藏版】
2019/10/15 Python
大四学生找工作的自荐信
2014/03/27 职场文书
学雷锋树新风演讲稿
2014/05/10 职场文书
应聘教师求职信
2014/07/19 职场文书
客户经理岗位职责大全
2015/04/09 职场文书
详解Redis瘦身指南
2021/05/26 Redis
MySQL提升大量数据查询效率的优化神器
2022/07/07 MySQL
TS 类型兼容教程示例详解
2022/09/23 Javascript