Go官方库的文件操作分散在多个包中,比如os
、ioutil
包,我本来想写一篇总结性的Go文件操作的文章,却发现已经有人2015年已经写了一篇这样的文章,写的非常好,所以我翻译成了中文,强烈推荐你阅读一下。
原文: Working with Files in Go, 作者: NanoDano
介绍
万物皆文件
UNIX 的一个基础设计就是"万物皆文件"(everything is a file)。我们不必知道一个文件到底映射成什么,操作系统的设备驱动抽象成文件。操作系统为设备提供了文件格式的接口。
Go语言中的reader和writer接口也类似。我们只需简单的读写字节,不必知道reader的数据来自哪里,也不必知道writer将数据发送到哪里。
你可以在/dev
下查看可用的设备,有些可能需要较高的权限才能访问。
基本操作
创建空文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
import (
"log"
"os"
)
var (
newFile *os.File
err error
)
func main() {
newFile, err = os.Create(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
log.Println(newFile)
newFile.Close()
}
|
Truncate文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import (
"log"
"os"
)
func main() {
err := os.Truncate(
"test.txt",
100)
if err !=
nil {
log.Fatal(err)
}
}
|
得到文件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import (
"fmt"
"log"
"os"
)
var (
fileInfo os.FileInfo
err error
)
func main() {
fileInfo, err = os.Stat(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
fmt.Println(
"File name:", fileInfo.Name())
fmt.Println(
"Size in bytes:", fileInfo.Size())
fmt.Println(
"Permissions:", fileInfo.Mode())
fmt.Println(
"Last modified:", fileInfo.ModTime())
fmt.Println(
"Is Directory: ", fileInfo.IsDir())
fmt.Printf(
"System interface type: %T\n", fileInfo.Sys())
fmt.Printf(
"System info: %+v\n\n", fileInfo.Sys())
}
|
重命名和移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import (
"log"
"os"
)
func main() {
originalPath :=
"test.txt"
newPath :=
"test2.txt"
err := os.Rename(originalPath, newPath)
if err !=
nil {
log.Fatal(err)
}
}
|
译者按: rename 和 move 原理一样
删除文件
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import (
"log"
"os"
)
func main() {
err := os.Remove(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
}
|
打开和关闭文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package main
import (
"log"
"os"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
file.Close()
file, err = os.OpenFile(
"test.txt", os.O_APPEND,
0666)
if err !=
nil {
log.Fatal(err)
}
file.Close()
}
|
译者按:熟悉Linux的读者应该很熟悉权限模式,通过Linux命令chmod
可以更改文件的权限
https://www.linux.com/learn/understanding-linux-file-permissions
补充了原文未介绍的flag
检查文件是否存在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package main
import (
"log"
"os"
)
var (
fileInfo *os.FileInfo
err error
)
func main() {
fileInfo, err := os.Stat(
"test.txt")
if err !=
nil {
if os.IsNotExist(err) {
log.Fatal(
"File does not exist.")
}
}
log.Println(
"File does exist. File information:")
log.Println(fileInfo)
}
|
检查读写权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import (
"log"
"os"
)
func main() {
file, err := os.OpenFile(
"test.txt", os.O_WRONLY,
0666)
if err !=
nil {
if os.IsPermission(err) {
log.Println(
"Error: Write permission denied.")
}
}
file.Close()
file, err = os.OpenFile(
"test.txt", os.O_RDONLY,
0666)
if err !=
nil {
if os.IsPermission(err) {
log.Println(
"Error: Read permission denied.")
}
}
file.Close()
}
|
改变权限、拥有者、时间戳
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package main
import (
"log"
"os"
"time"
)
func main() {
err := os.Chmod(
"test.txt",
0777)
if err !=
nil {
log.Println(err)
}
err = os.Chown(
"test.txt", os.Getuid(), os.Getgid())
if err !=
nil {
log.Println(err)
}
twoDaysFromNow := time.Now().Add
(48 * time.Hour)
lastAccessTime := twoDaysFromNow
lastModifyTime := twoDaysFromNow
err = os.Chtimes(
"test.txt", lastAccessTime, lastModifyTime)
if err !=
nil {
log.Println(err)
}
}
|
硬链接和软链接
一个普通的文件是一个指向硬盘的inode的地方。
硬链接创建一个新的指针指向同一个地方。只有所有的链接被删除后文件才会被删除。硬链接只在相同的文件系统中才工作。你可以认为一个硬链接是一个正常的链接。
symbolic link,又叫软连接,和硬链接有点不一样,它不直接指向硬盘中的相同的地方,而是通过名字引用其它文件。他们可以指向不同的文件系统中的不同文件。并不是所有的操作系统都支持软链接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package main
import (
"os"
"log"
"fmt"
)
func main() {
err := os.Link(
"original.txt",
"original_also.txt")
if err !=
nil {
log.Fatal(err)
}
fmt.Println(
"creating sym")
err = os.Symlink(
"original.txt",
"original_sym.txt")
if err !=
nil {
log.Fatal(err)
}
fileInfo, err := os.Lstat(
"original_sym.txt")
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Link info: %+v", fileInfo)
err = os.Lchown(
"original_sym.txt", os.Getuid(), os.Getgid())
if err !=
nil {
log.Fatal(err)
}
}
|
读写
复制文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package main
import (
"os"
"log"
"io"
)
func main() {
originalFile, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
defer originalFile.Close()
newFile, err := os.Create(
"test_copy.txt")
if err !=
nil {
log.Fatal(err)
}
defer newFile.Close()
bytesWritten, err := io.Copy(newFile, originalFile)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Copied %d bytes.", bytesWritten)
err = newFile.Sync()
if err !=
nil {
log.Fatal(err)
}
}
|
跳转到文件指定位置(Seek)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package main
import (
"os"
"fmt"
"log"
)
func main() {
file, _ := os.Open(
"test.txt")
defer file.Close()
var offset
int64 =
5
var whence
int =
0
newPosition, err := file.Seek(offset, whence)
if err !=
nil {
log.Fatal(err)
}
fmt.Println(
"Just moved to 5:", newPosition)
newPosition, err = file.Seek
(-2,
1)
if err !=
nil {
log.Fatal(err)
}
fmt.Println(
"Just moved back two:", newPosition)
currentPosition, err := file.Seek
(0,
1)
fmt.Println(
"Current position:", currentPosition)
newPosition, err = file.Seek
(0,
0)
if err !=
nil {
log.Fatal(err)
}
fmt.Println(
"Position after seeking 0,0:", newPosition)
}
|
写文件
可以使用os
包写入一个打开的文件。
因为Go可执行包是静态链接的可执行文件,你import的每一个包都会增加你的可执行文件的大小。其它的包如io
、`ioutil`、`bufio`提供了一些方法,但是它们不是必须的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import (
"os"
"log"
)
func main() {
file, err := os.OpenFile(
"test.txt",
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666,
)
if err !=
nil {
log.Fatal(err)
}
defer file.Close()
byteSlice := []
byte(
"Bytes!\n")
bytesWritten, err := file.Write(byteSlice)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Wrote %d bytes.\n", bytesWritten)
}
|
快写文件
ioutil
包有一个非常有用的方法WriteFile()
可以处理创建/打开文件、写字节slice和关闭文件一系列的操作。如果你需要简洁快速地写字节slice到文件中,你可以使用它。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import (
"io/ioutil"
"log"
)
func main() {
err := ioutil.WriteFile(
"test.txt", []
byte(
"Hi\n"),
0666)
if err !=
nil {
log.Fatal(err)
}
}
|
使用缓存写
bufio
包提供了带缓存功能的writer,所以你可以在写字节到硬盘前使用内存缓存。当你处理很多的数据很有用,因为它可以节省操作硬盘I/O的时间。在其它一些情况下它也很有用,比如你每次写一个字节,把它们攒在内存缓存中,然后一次写入到硬盘中,减少硬盘的磨损以及提升性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
package main
import (
"log"
"os"
"bufio"
)
func main() {
file, err := os.OpenFile(
"test.txt", os.O_WRONLY,
0666)
if err !=
nil {
log.Fatal(err)
}
defer file.Close()
bufferedWriter := bufio.NewWriter(file)
bytesWritten, err := bufferedWriter.Write(
[]
byte
{65,
66,
67},
)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Bytes written: %d\n", bytesWritten)
bytesWritten, err = bufferedWriter.WriteString(
"Buffered string\n",
)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Bytes written: %d\n", bytesWritten)
unflushedBufferSize := bufferedWriter.Buffered()
log.Printf(
"Bytes buffered: %d\n", unflushedBufferSize)
bytesAvailable := bufferedWriter.Available()
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Available buffer: %d\n", bytesAvailable)
bufferedWriter.Flush()
bufferedWriter.Reset(bufferedWriter)
bytesAvailable = bufferedWriter.Available()
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Available buffer: %d\n", bytesAvailable)
bufferedWriter = bufio.NewWriterSize(
bufferedWriter,
8000,
)
bytesAvailable = bufferedWriter.Available()
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Available buffer: %d\n", bytesAvailable)
}
|
读取最多N个字节
os.File
提供了文件操作的基本功能, 而io
、ioutil
、bufio
提供了额外的辅助函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package main
import (
"os"
"log"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
defer file.Close()
byteSlice :=
make([]
byte,
16)
bytesRead, err := file.Read(byteSlice)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Number of bytes read: %d\n", bytesRead)
log.Printf(
"Data read: %s\n", byteSlice)
}
|
读取正好N个字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import (
"os"
"log"
"io"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
byteSlice :=
make([]
byte,
2)
numBytesRead, err := io.ReadFull(file, byteSlice)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Number of bytes read: %d\n", numBytesRead)
log.Printf(
"Data read: %s\n", byteSlice)
}
|
读取至少N个字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import (
"os"
"log"
"io"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
byteSlice :=
make([]
byte,
512)
minBytes :=
8
numBytesRead, err := io.ReadAtLeast(file, byteSlice, minBytes)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Number of bytes read: %d\n", numBytesRead)
log.Printf(
"Data read: %s\n", byteSlice)
}
|
读取全部字节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import (
"os"
"log"
"fmt"
"io/ioutil"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(file)
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Data as hex: %x\n", data)
fmt.Printf(
"Data as string: %s\n", data)
fmt.Println(
"Number of bytes read:",
len(data))
}
|
快读到内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import (
"log"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Data read: %s\n", data)
}
|
使用缓存读
有缓存写也有缓存读。
缓存reader会把一些内容缓存在内存中。它会提供比os.File
和io.Reader
更多的函数,缺省的缓存大小是4096,最小缓存是16。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package main
import (
"os"
"log"
"bufio"
"fmt"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
bufferedReader := bufio.NewReader(file)
byteSlice :=
make([]
byte,
5)
byteSlice, err = bufferedReader.Peek
(5)
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Peeked at 5 bytes: %s\n", byteSlice)
numBytesRead, err := bufferedReader.Read(byteSlice)
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Read %d bytes: %s\n", numBytesRead, byteSlice)
myByte, err := bufferedReader.ReadByte()
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Read 1 byte: %c\n", myByte)
dataBytes, err := bufferedReader.ReadBytes(
'\n')
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Read bytes: %s\n", dataBytes)
dataString, err := bufferedReader.ReadString(
'\n')
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Read string: %s\n", dataString)
}
|
使用 scanner
Scanner
是bufio
包下的类型,在处理文件中以分隔符分隔的文本时很有用。
通常我们使用换行符作为分隔符将文件内容分成多行。在CSV文件中,逗号一般作为分隔符。
os.File
文件可以被包装成bufio.Scanner
,它就像一个缓存reader。
我们会调用Scan()
方法去读取下一个分隔符,使用Text()
或者Bytes()
获取读取的数据。
分隔符可以不是一个简单的字节或者字符,有一个特殊的方法可以实现分隔符的功能,以及将指针移动多少,返回什么数据。
如果没有定制的SplitFunc
提供,缺省的ScanLines
会使用newline
字符作为分隔符,其它的分隔函数还包括ScanRunes
和ScanWords
,皆在bufio
包中。
1
2
3
4
5
6
|
type SplitFunc
func(data []
byte, atEOF
bool) (advance
int, token []
byte, err error)
|
下面的例子中,为一个文件创建了bufio.Scanner
,并按照单词逐个读取:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package main
import (
"os"
"log"
"fmt"
"bufio"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)
success := scanner.Scan()
if success ==
false {
err = scanner.Err()
if err ==
nil {
log.Println(
"Scan completed and reached EOF")
}
else {
log.Fatal(err)
}
}
fmt.Println(
"First word found:", scanner.Text())
}
|
压缩
打包(zip) 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package main
import (
"archive/zip"
"log"
"os"
)
func main() {
outFile, err := os.Create(
"test.zip")
if err !=
nil {
log.Fatal(err)
}
defer outFile.Close()
zipWriter := zip.NewWriter(outFile)
var filesToArchive = []
struct {
Name, Body
string
} {
{
"test.txt",
"String contents of file"},
{
"test2.txt",
"\x61\x62\x63\n"},
}
for _, file :=
range filesToArchive {
fileWriter, err := zipWriter.Create(file.Name)
if err !=
nil {
log.Fatal(err)
}
_, err = fileWriter.Write([]
byte(file.Body))
if err !=
nil {
log.Fatal(err)
}
}
err = zipWriter.Close()
if err !=
nil {
log.Fatal(err)
}
}
|
抽取(unzip) 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package main
import (
"archive/zip"
"log"
"io"
"os"
"path/filepath"
)
func main() {
zipReader, err := zip.OpenReader(
"test.zip")
if err !=
nil {
log.Fatal(err)
}
defer zipReader.Close()
for _, file :=
range zipReader.Reader.File {
zippedFile, err := file.Open()
if err !=
nil {
log.Fatal(err)
}
defer zippedFile.Close()
targetDir :=
"./"
extractedFilePath := filepath.Join(
targetDir,
file.Name,
)
if file.FileInfo().IsDir() {
log.Println(
"Creating directory:", extractedFilePath)
os.MkdirAll(extractedFilePath, file.Mode())
}
else {
log.Println(
"Extracting file:", file.Name)
outputFile, err := os.OpenFile(
extractedFilePath,
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
file.Mode(),
)
if err !=
nil {
log.Fatal(err)
}
defer outputFile.Close()
_, err = io.Copy(outputFile, zippedFile)
if err !=
nil {
log.Fatal(err)
}
}
}
}
|
压缩文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import (
"os"
"compress/gzip"
"log"
)
func main() {
outputFile, err := os.Create(
"test.txt.gz")
if err !=
nil {
log.Fatal(err)
}
gzipWriter := gzip.NewWriter(outputFile)
defer gzipWriter.Close()
_, err = gzipWriter.Write([]
byte(
"Gophers rule!\n"))
if err !=
nil {
log.Fatal(err)
}
log.Println(
"Compressed data written to file.")
}
|
解压缩文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package main
import (
"compress/gzip"
"log"
"io"
"os"
)
func main() {
gzipFile, err := os.Open(
"test.txt.gz")
if err !=
nil {
log.Fatal(err)
}
gzipReader, err := gzip.NewReader(gzipFile)
if err !=
nil {
log.Fatal(err)
}
defer gzipReader.Close()
outfileWriter, err := os.Create(
"unzipped.txt")
if err !=
nil {
log.Fatal(err)
}
defer outfileWriter.Close()
_, err = io.Copy(outfileWriter, gzipReader)
if err !=
nil {
log.Fatal(err)
}
}
|
其它
临时文件和目录
ioutil
提供了两个函数: TempDir()
和 TempFile()
。
使用完毕后,调用者负责删除这些临时文件和文件夹。
有一点好处就是当你传递一个空字符串作为文件夹名的时候,它会在操作系统的临时文件夹中创建这些项目(/tmp on Linux)。
os.TempDir()
返回当前操作系统的临时文件夹。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package main
import (
"os"
"io/ioutil"
"log"
"fmt"
)
func main() {
tempDirPath, err := ioutil.TempDir(
"",
"myTempDir")
if err !=
nil {
log.Fatal(err)
}
fmt.Println(
"Temp dir created:", tempDirPath)
tempFile, err := ioutil.TempFile(tempDirPath,
"myTempFile.txt")
if err !=
nil {
log.Fatal(err)
}
fmt.Println(
"Temp file created:", tempFile.Name())
err = tempFile.Close()
if err !=
nil {
log.Fatal(err)
}
err = os.Remove(tempFile.Name())
if err !=
nil {
log.Fatal(err)
}
err = os.Remove(tempDirPath)
if err !=
nil {
log.Fatal(err)
}
}
|
通过HTTP下载文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package main
import (
"os"
"io"
"log"
"net/http"
)
func main() {
newFile, err := os.Create(
"devdungeon.html")
if err !=
nil {
log.Fatal(err)
}
defer newFile.Close()
url :=
"http://www.devdungeon.com/archive"
response, err := http.Get(url)
defer response.Body.Close()
numBytesWritten, err := io.Copy(newFile, response.Body)
if err !=
nil {
log.Fatal(err)
}
log.Printf(
"Downloaded %d byte file.\n", numBytesWritten)
}
|
哈希和摘要
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"log"
"fmt"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
fmt.Printf(
"Md5: %x\n\n", md5.Sum(data))
fmt.Printf(
"Sha1: %x\n\n", sha1.Sum(data))
fmt.Printf(
"Sha256: %x\n\n", sha256.Sum256(data))
fmt.Printf(
"Sha512: %x\n\n", sha512.Sum512(data))
}
|
上面的例子复制整个文件内容到内存中,传递给hash函数。
另一个方式是创建一个hash writer, 使用Write
、WriteString
、Copy
将数据传给它。
下面的例子使用 md5 hash,但你可以使用其它的Writer。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package main
import (
"crypto/md5"
"log"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open(
"test.txt")
if err !=
nil {
log.Fatal(err)
}
defer file.Close()
hasher := md5.New()
_, err = io.Copy(hasher, file)
if err !=
nil {
log.Fatal(err)
}
sum := hasher.Sum(
nil)
fmt.Printf(
"Md5 checksum: %x\n", sum)
}
|
文章来源 http://colobu.com/2016/10/12/go-file-operations/ 一个非常好的个人技术blog