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 相关文章推荐
为什么不建议在go项目中使用init()
Apr 12 Golang
解决golang post文件时Content-Type出现的问题
May 02 Golang
golang 比较浮点数的大小方式
May 02 Golang
解决goland 导入项目后import里的包报红问题
May 06 Golang
golang 实现并发求和
May 08 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
Go语言基础map用法及示例详解
Nov 17 Golang
Go归并排序算法的实现方法
Apr 06 Golang
golang操作redis的客户端包有多个比如redigo、go-redis
Apr 14 Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 Golang
在ubuntu下安装go开发环境的全过程
Aug 05 Golang
go goth封装第三方认证库示例详解
Aug 14 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 二维数组排序保持键名不变
2019/03/06 PHP
php屏蔽错误及提示的方法
2020/05/10 PHP
[原创]网络复制内容时常用的正则+editplus
2006/11/30 Javascript
Javascript脚本实现静态网页加密实例代码
2013/11/05 Javascript
JavaScript1.6数组新特性介绍以及JQuery的几个工具方法
2013/12/06 Javascript
IE6-IE9中tbody的innerHTML不能赋值的解决方法
2014/06/05 Javascript
node.js中的fs.chmod方法使用说明
2014/12/18 Javascript
javascript中setInterval的用法
2015/07/19 Javascript
浅谈javascript中的加减时间
2016/07/12 Javascript
jQuery新窗口打开外链接
2016/07/21 Javascript
AngularJs中 ng-repeat指令中实现含有自定义指令的动态html的方法
2017/01/19 Javascript
移动端使用localResizeIMG4压缩图片
2017/04/22 Javascript
详解10分钟学会vue滚动行为
2017/09/21 Javascript
bmob js-sdk 在vue中的使用教程
2018/01/21 Javascript
mpvue项目中使用第三方UI组件库的方法
2018/09/30 Javascript
webpack 静态资源集中输出的方法示例
2018/11/09 Javascript
[原创]微信小程序获取网络类型的方法示例
2019/03/01 Javascript
webpack 如何解析代码模块路径的实现
2019/09/04 Javascript
解决layui数据表格table的横向滚动条显示问题
2019/09/04 Javascript
微信小程序input抖动问题的修复方法
2021/03/03 Javascript
Python读取英文文件并记录每个单词出现次数后降序输出示例
2018/06/28 Python
Python迭代器与生成器用法实例分析
2018/07/09 Python
python使用udp实现聊天器功能
2018/12/10 Python
python变量的存储原理详解
2019/07/10 Python
wxpython布局的实现方法
2019/11/01 Python
python中从for循环延申到推导式的具体使用
2019/11/29 Python
从训练好的tensorflow模型中打印训练变量实例
2020/01/20 Python
详解用Python调用百度地图正/逆地理编码API
2020/07/02 Python
Python之字典对象的几种创建方法
2020/09/30 Python
Python实现自动装机功能案例分析
2020/10/22 Python
HUGO BOSS美国官方网上商店:世界知名奢侈品牌
2017/08/04 全球购物
简单说说tomcat的配置
2013/05/28 面试题
临床医学大学生求职信
2013/09/28 职场文书
十八届三中全会报告学习材料
2014/02/17 职场文书
热门专业求职信
2014/05/24 职场文书
关心下一代工作先进事迹
2014/08/15 职场文书