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 interface{}==nil 的几种坑及原理分析
Apr 24 Golang
Golang 实现超大文件读取的两种方法
Apr 27 Golang
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 Golang
golang 实现Location跳转方式
May 02 Golang
golang协程池模拟实现群发邮件功能
May 02 Golang
go语言基础 seek光标位置os包的使用
May 09 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
Golang二维数组的使用方式
May 28 Golang
go语言中http超时引发的事故解决
Jun 02 Golang
Go 语言结构实例分析
Jul 04 Golang
Golang 链表的学习和使用
Apr 19 Golang
GoFrame基于性能测试得知grpool使用场景
Jun 21 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 cURL和Rolling cURL并发方式比较
2013/10/30 PHP
什么情况下可以不写PHP的闭合标签“?&gt;”
2014/08/28 PHP
php中实现记住密码下次自动登录的例子
2014/11/06 PHP
php读取mssql的ntext字段返回值为空的解决方法
2014/12/30 PHP
在WordPress中实现评论头像的自定义默认和延迟加载
2015/11/24 PHP
php微信公众号开发模式详解
2016/11/28 PHP
ecshop添加菜单及权限分配问题
2017/11/21 PHP
PHP文件后缀不强制为.php方法
2019/03/31 PHP
基于Laravel-admin 后台的自定义页面用法详解
2019/09/30 PHP
基于jQuery的Spin Button自定义文本框数值自增或自减
2010/07/17 Javascript
js判断输入是否为正整数、浮点数等数字的函数代码
2010/11/17 Javascript
Javascript基础教程之数据类型 (布尔型 Boolean)
2015/01/18 Javascript
jquery中$each()方法的使用指南
2015/04/30 Javascript
JavaScript使用FileSystemObject对象写入文本文件内容的方法
2015/08/05 Javascript
JS实现动态增加和删除li标签行的实例代码
2016/10/16 Javascript
Nodejs 发送Post请求功能(发短信验证码例子)
2017/02/09 NodeJs
Express URL跳转(重定向)的实现方法
2017/04/07 Javascript
详解vue中使用express+fetch获取本地json文件
2017/10/10 Javascript
Angular使用cli生成自定义文件、组件的方法
2018/09/04 Javascript
react+ant design实现Table的增、删、改的示例代码
2018/12/27 Javascript
微信小程序中使用Async-await方法异步请求变为同步请求方法
2019/03/28 Javascript
layui实现左侧菜单点击右侧内容区显示
2019/07/26 Javascript
基于Element的组件改造的树形选择器(树形下拉框)
2020/02/27 Javascript
详解使用 pyenv 管理多个版本 python 环境
2017/10/19 Python
python zip()函数使用方法解析
2019/10/31 Python
python 连续不等式语法糖实例
2020/04/15 Python
HTML5公共页面提取作为公用代码的方法
2020/06/30 HTML / CSS
前端水印的简单实现代码示例
2020/12/02 HTML / CSS
澳大利亚时尚前卫设计师珠宝在线:Amber Sceats
2017/10/04 全球购物
高中校园广播稿
2014/01/11 职场文书
六年级学生评语
2014/04/22 职场文书
2015年八一建军节活动总结
2015/03/20 职场文书
2015年学校教科室工作总结
2015/07/20 职场文书
银行安全保卫工作总结
2015/08/10 职场文书
关于践行三严三实的心得体会
2016/01/05 职场文书
TypeScript 使用 Tuple Union 声明函数重载
2022/04/07 Javascript