我的应用场景是:并发的读取一个文件(或者一个超大的文件),计算文件的hash值,
就比如:文件60G,你的内存4G,如何计算?
这个例子是不是不太可能实现:
func main() {
ffff, err := os.Open("./2.txt")
if err != nil {
fmt.Println("读取文件失败!")
}
defer ffff.Close()
hash := md5.New()
if _, err := io.Copy(hash, ffff); err != nil {
log.Fatal(err)
}
sum := hash.Sum(nil)
fmt.Printf("%x\n", sum)
}
这是平常的方法,对于超大文件是肯定不合适的
好了 ,直接上代码,这是我做的,可能有点小瑕疵,待解决,大神们也可直接指点迷津~~~
import (
"bufio"
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"log"
"os"
"sync"
"time"
)
// 多线程读取大文件 计算hash值
// 分片计算的结果
var dictResult sync.Map
// 是否已经读取结束
var fileEnd chan struct{}
// 并发控制
var dictThread chan struct{}
// 文件路径
var filePath = "./2.txt"
// 分片读取大小
var readSize = 20
// 并发的线程数量
var threadNum = 3
func main() {
dictThread = make(chan struct{}, threadNum)
fileEnd = make(chan struct{})
for i := 0; ; i++ {
t := i
dictThread <- struct{}{}
go readFileByByte(filePath, t*readSize-1, i)
select {
case <-fileEnd:
goto Next
default:
}
}
Next:
time.Sleep(time.Second)
ret := make([]string, 0)
for i := 0; ; i++ {
value, ok := dictResult.LoadAndDelete(i)
if !ok {
break
}
ret = append(ret, value.(string))
}
r := md5.Sum([]byte(buildString(ret...)))
fmt.Println(hex.EncodeToString(r[:]))
}
// 按字节读取文件: 文件路径,偏移量,第几片,
func readFileByByte(filePath string, set int, pageNum int) {
f, err := os.Open(filePath)
if err != nil {
log.Fatal(err)
return
}
if set == -1 {
set = 0
}
_, err = f.Seek(int64(set), io.SeekStart)
if err != nil {
f.Close()
fmt.Println(err.Error())
fileEnd <- struct{}{} // 结束了
return
}
var buf = make([]byte, 4096)
reader := bufio.NewReader(f)
n, err := reader.Read(buf)
f.Close()
<-dictThread
if err != nil && err != io.EOF || n == 0 {
fileEnd <- struct{}{} // 结束了
return
}
// md5 计算
sum := md5.Sum(buf[:n])
dictResult.Store(pageNum, hex.EncodeToString(sum[:]))
fmt.Println(pageNum, "==>>", hex.EncodeToString(sum[:]))
return
}
func buildString(pieces ...string) string {
var strBuf bytes.Buffer
for _, piece := range pieces {
strBuf.WriteString(piece)
}
return strBuf.String()
}