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中ResponseWriter的一个坑
Apr 27 Golang
Golang 空map和未初始化map的注意事项说明
Apr 29 Golang
golang elasticsearch Client的使用详解
May 05 Golang
Golang之sync.Pool使用详解
May 06 Golang
go 实现简易端口扫描的示例
May 22 Golang
Go 语言中 20 个占位符的整理
Oct 16 Golang
Golang流模式之grpc的四种数据流
Apr 13 Golang
Go语言grpc和protobuf
Apr 13 Golang
golang生成并解析JSON
Apr 14 Golang
Golang日志包的使用
Apr 20 Golang
Golang 切片(Slice)实现增删改查
Apr 22 Golang
Golang 实现WebSockets
Apr 24 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 采集程序中常用的函数
2009/12/09 PHP
《PHP编程最快明白》第二讲 数字、浮点、布尔型、字符串和数组
2010/11/01 PHP
PHP实现C#山寨ArrayList的方法
2015/07/16 PHP
php字符串比较函数用法小结(strcmp,strcasecmp,strnatcmp及strnatcasecmp)
2016/07/18 PHP
thinkPHP自动验证、自动添加及表单错误问题分析
2016/10/17 PHP
PHP实现通过二维数组键值获取一维键名操作示例
2019/10/11 PHP
jquery动画2.元素坐标动画效果(创建一个图片走廊)
2012/08/24 Javascript
尝试在让script的type属性等于text/html
2013/01/15 Javascript
jquery 设置元素相对于另一个元素的top值(实例代码)
2013/11/06 Javascript
jQuery中append()方法用法实例
2015/01/08 Javascript
jQuery中dom元素上绑定的事件详解
2015/04/24 Javascript
JQuery+CSS实现图片上放置按钮的方法
2015/05/29 Javascript
14款经典网页图片和文字特效的jQuery插件-前端开发必备
2015/08/25 Javascript
jQuery实现仿QQ头像闪烁效果的文字闪动提示代码
2015/11/03 Javascript
JS实现部分HTML固定页面顶部随屏滚动效果
2015/12/24 Javascript
基于JavaScript实现简单的随机抽奖小程序
2016/01/05 Javascript
javascript解析ajax返回的xml和json格式数据实例详解
2017/01/05 Javascript
分享vue.js devtools遇到一系列问题
2017/10/24 Javascript
Vue 动态组件components和v-once指令的实现
2019/08/30 Javascript
javascript网页随机点名实现过程解析
2019/10/15 Javascript
Jquery高级应用Deferred对象原理及使用实例
2020/05/28 jQuery
jQuery实现简单飞机大战
2020/07/05 jQuery
Python中max函数用法实例分析
2015/07/17 Python
详解通过API管理或定制开发ECS实例
2018/09/30 Python
pyspark操作MongoDB的方法步骤
2019/01/04 Python
提升python处理速度原理及方法实例
2019/12/25 Python
python+OpenCV实现图像拼接
2020/03/05 Python
几个解决兼容IE6\7\8不支持html5标签的几个方法
2013/01/07 HTML / CSS
canvas学习和滤镜实现代码
2018/08/22 HTML / CSS
New Balance俄罗斯官方网上商店:购买运动鞋
2020/03/02 全球购物
美国室内盆栽植物购买网站:Plants.com
2020/04/24 全球购物
计算机毕业生自荐信
2014/06/12 职场文书
中国梦口号
2014/06/13 职场文书
2014年酒店工作总结范文
2014/11/17 职场文书
2015年银行工作总结范文
2015/04/01 职场文书
Python if else条件语句形式详解
2022/03/24 Python