一娘扩、File文件操作
file類是在os包中的帮掉,封裝了底層的文件描述符和相關(guān)信息誊辉,同時封裝了Read和Write的實現(xiàn)。
1恼蓬、FileInfo接口
FileInfo接口中定義了File信息相關(guān)的方法惊完。
go源碼:
// os包的Stat方法返回FileInfo接口
func Stat(name string) (FileInfo, error) {}
// FileInfo接口提供了獲得文件信息的方法
type FileInfo interface {
Name() string // base name of the file 文件名.擴(kuò)展名 aa.txt
Size() int64 // 文件大小,字節(jié)數(shù) 12540
Mode() FileMode // 文件權(quán)限 -rw-rw-rw-
ModTime() time.Time // 修改時間 2018-04-13 16:30:53 +0800 CST
IsDir() bool // 是否文件夾
Sys() interface{} // 基礎(chǔ)數(shù)據(jù)源接口(can return nil)
}
2处硬、權(quán)限
至于操作權(quán)限perm小槐,除非創(chuàng)建文件時才需要指定,不需要創(chuàng)建新文件時可以將其設(shè)定為0荷辕。雖然go語言給perm權(quán)限設(shè)定了很多的常量凿跳,但是習(xí)慣上也可以直接使用數(shù)字,如0666(具體含義和Unix系統(tǒng)的一致)桐腌。
go定義了一個權(quán)限常量ModePerm FileMode = 0777拄显,使用os.ModePerm可以獲取。
3案站、打開模式
文件打開模式:
const (
O_RDONLY int = syscall.O_RDONLY // 只讀模式打開文件
O_WRONLY int = syscall.O_WRONLY // 只寫模式打開文件
O_RDWR int = syscall.O_RDWR // 讀寫模式打開文件
O_APPEND int = syscall.O_APPEND // 寫操作時將數(shù)據(jù)附加到文件尾部
O_CREATE int = syscall.O_CREAT // 如果不存在將創(chuàng)建一個新文件
O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用躬审,文件必須不存在
O_SYNC int = syscall.O_SYNC // 打開文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // 如果可能,打開時清空文件
)
4蟆盐、文件操作
go源碼:
package filepath
// IsAbs reports whether the path is absolute.
func IsAbs(path string) (b bool) {}
// Abs returns an absolute representation of path.
func Abs(path string) (string, error) {
return abs(path)
}
package os
// File 代表一個打開的文件對象
type File struct {
*file // os specific
}
// Open打開一個文件用于讀取承边。
// 如果操作成功,返回的文件對象的方法可用于讀取數(shù)據(jù)石挂;對應(yīng)的文件描述符具有O_RDONLY模式博助。
// 如果出錯,錯誤底層類型是*PathError痹愚。
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
// OpenFile是一個更一般性的文件打開函數(shù)富岳,大多數(shù)調(diào)用者都應(yīng)用Open或Create代替本函數(shù)。
// 它會使用指定的選項(如O_RDONLY等)拯腮、指定的模式(如0666等)打開指定名稱的文件窖式。
// 如果操作成功,返回的文件對象可用于I/O动壤。如果出錯萝喘,錯誤底層類型是*PathError。
func OpenFile(name string, flag int, perm FileMode) (*File, error) {}
// Create采用模式0666(任何人都可讀寫,不可執(zhí)行)創(chuàng)建一個名為name的文件阁簸,如果文件已存在會截斷它(為空文件)爬早。
// 如果成功,返回的文件對象可用于I/O启妹;對應(yīng)的文件描述符具有O_RDWR模式筛严。如果出錯,錯誤底層類型是*PathError翅溺。
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
// 創(chuàng)建文件夾脑漫,如果文件夾存在髓抑,創(chuàng)建失敗
func Mkdir(name string, perm FileMode) error {}
// os.MkDirAll()咙崎,可以創(chuàng)建多層
func MkdirAll(path string, perm FileMode) error {}
// 刪除文件或目錄:慎用,慎用吨拍,再慎用
func Remove(name string) error {}
// 刪除所有
func RemoveAll(path string) error {}
//Name方法返回文件名稱褪猛。
func (f *File) Name() string { return f.name }
//Stat返回描述文件f的FileInfo類型值。如果出錯羹饰,錯誤底層類型是*PathError伊滋。
func (file *File) Stat() (FileInfo, error) {}
// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {}
// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {}
// Close關(guān)閉文件f,使文件不能用于讀寫队秩。如果文件已經(jīng)關(guān)閉返回錯誤:file already closed笑旺。
func (f *File) Close() error {}
二、i/o操作
I/O操作也叫輸入輸出操作馍资。其中I是指Input筒主,O是指Output,用于讀或者寫數(shù)據(jù)的鸟蟹,有些語言中也叫流操作乌妙,是指數(shù)據(jù)通信的通道。
1建钥、i/o包
io包中提供I/O原始操作的一系列接口藤韵。它主要包裝了一些已有的實現(xiàn),如 os 包中的那些熊经,并將這些抽象成為實用性的功能和一些其他相關(guān)的接口泽艘。
在io包中最重要的是兩個接口:Reader和Writer接口
- Reader接口的定義,Read()方法用于讀取數(shù)據(jù)镐依。
type Reader interface {
Read(p []byte) (n int, err error)
}
示例代碼:
package main
import (
"fmt"
"io"
"os"
)
func main() {
filePath := "aa.txt"
// 1.打開文件
f, err := os.Open(filePath)
if err != nil {
fmt.Println("err:", err)
return
}
// 2.延遲關(guān)閉文件
defer f.Close()
// 3.讀取文件
// 3.1 創(chuàng)建一個byte類型的切片匹涮,用于存儲讀到的數(shù)據(jù)
temp := make([]byte, 4)
// 3.2 循環(huán)讀取文件內(nèi)容
for {
n, err := f.Read(temp)
if n == 0 || err == io.EOF {
fmt.Println("文件讀取完了")
return
}
if err != nil && err != io.EOF {
fmt.Println("err: ", err)
return
}
fmt.Printf("讀取到了%d個字節(jié).\n", n)
fmt.Println(string(temp[:n]))
}
}
運行結(jié)果
讀取到了4個字節(jié).
hell
讀取到了4個字節(jié).
o wo
讀取到了3個字節(jié).
rld
文件讀取完了
- Writer接口的定義,Write()方法用于寫出數(shù)據(jù)馋吗。
type Writer interface {
Write(p []byte) (n int, err error)
}
示例代碼
package main
import (
"fmt"
"os"
)
func main() {
filePath := "ab.txt"
// 1.打開文件(以可讀可寫的方式)
f, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, os.ModePerm)
if err != nil {
fmt.Println("err: ", err)
return
}
// 2.延遲關(guān)閉文件
defer f.Close()
// 3.寫入內(nèi)容文件
n, err := f.Write([]byte("hello world"))
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Printf("寫入%d個字節(jié)\n", n)
n, err = f.WriteString("你好 世界")
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Printf("寫入%d個字節(jié)\n", n)
}
運行結(jié)果
寫入11個字節(jié)
寫入13個字節(jié)
三焕盟、bufio包
Go語言在io操作中,還提供了一個bufio的包,使用這個包可以大幅提高文件讀寫的效率脚翘。
1灼卢、bufio包原理
bufio 是通過緩沖來提高效率。
io操作本身的效率并不低来农,低的是頻繁的訪問本地磁盤的文件鞋真。所以bufio就提供了緩沖區(qū)(分配一塊內(nèi)存),讀和寫都先在緩沖區(qū)中沃于,最后再讀寫文件涩咖,來降低訪問本地磁盤的次數(shù),從而提高效率繁莹。
bufio 封裝了io.Reader或io.Writer接口對象檩互,并創(chuàng)建另一個也實現(xiàn)了該接口的對象搭儒。
io.Reader或io.Writer 接口實現(xiàn)read() 和 write() 方法赖舟,對于實現(xiàn)這個接口的對象都是可以使用這兩個方法的。
Reader對象
bufio.Reader 是bufio中對io.Reader 的封裝
// Reader implements buffering for an io.Reader object.
type Reader struct {
buf []byte
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int // last byte read for UnreadByte; -1 means invalid
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}
bufio.Read(p []byte) 相當(dāng)于讀取大小len(p)的內(nèi)容况鸣,思路如下:
- 當(dāng)緩存區(qū)有內(nèi)容的時薄风,將緩存區(qū)內(nèi)容全部填入p并清空緩存區(qū)
- 當(dāng)緩存區(qū)沒有內(nèi)容的時候且len(p)>len(buf),即要讀取的內(nèi)容比緩存區(qū)還要大饵较,直接去文件讀取即可
- 當(dāng)緩存區(qū)沒有內(nèi)容的時候且len(p)<len(buf),即要讀取的內(nèi)容比緩存區(qū)小,緩存區(qū)從文件讀取內(nèi)容充滿緩存區(qū)遭赂,并將p填滿(此時緩存區(qū)有剩余內(nèi)容)
- 以后再次讀取時緩存區(qū)有內(nèi)容循诉,將緩存區(qū)內(nèi)容全部填入p并清空緩存區(qū)(此時和情況1一樣)
Writer對象
bufio.Writer 是bufio中對io.Writer 的封裝
// Writer implements buffering for an io.Writer object.
// If an error occurs writing to a Writer, no more data will be
// accepted and all subsequent writes, and Flush, will return the error.
// After all data has been written, the client should call the
// Flush method to guarantee all data has been forwarded to
// the underlying io.Writer.
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}
bufio.Write(p []byte) 的思路如下
- 判斷buf中可用容量是否可以放下 p
- 如果能放下,直接把p拼接到buf后面撇他,即把內(nèi)容放到緩沖區(qū)
- 如果緩沖區(qū)的可用容量不足以放下茄猫,且此時緩沖區(qū)是空的,直接把p寫入文件即可
- 如果緩沖區(qū)的可用容量不足以放下逆粹,且此時緩沖區(qū)有內(nèi)容募疮,則用p把緩沖區(qū)填滿,把緩沖區(qū)所有內(nèi)容寫入文件僻弹,并清空緩沖區(qū)
- 判斷p的剩余內(nèi)容大小能否放到緩沖區(qū)阿浓,如果能放下(此時和步驟1情況一樣)則把內(nèi)容放到緩沖區(qū)
- 如果p的剩余內(nèi)容依舊大于緩沖區(qū),(注意此時緩沖區(qū)是空的蹋绽,情況和步驟3一樣)則把p的剩余內(nèi)容直接寫入文件芭毙。
2、bufio包
bufio包實現(xiàn)了有緩沖的I/O卸耘。它包裝一個io.Reader或io.Writer接口對象退敦,創(chuàng)建另一個也實現(xiàn)了該接口,且同時還提供了緩沖和一些文本I/O的幫助函數(shù)的對象蚣抗。
- bufie.Reader:
// NewReaderSize 將 rd 封裝成一個帶緩存的 bufio.Reader 對象侈百,
// 緩存大小由 size 指定(如果小于 16 則會被設(shè)置為 16)。
// 如果 rd 的基類型就是有足夠緩存的 bufio.Reader 類型,則直接將
// rd 轉(zhuǎn)換為基類型返回钝域。
func NewReaderSize(rd io.Reader, size int) *Reader{}
// NewReader 相當(dāng)于 NewReaderSize(rd, 4096)
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
// Read 從 b 中讀出數(shù)據(jù)到 p 中讽坏,返回讀出的字節(jié)數(shù)和遇到的錯誤。
func (b *Reader) Read(p []byte) (n int, err error) {}
// ReadByte讀取并返回一個字節(jié).
// 如果沒有可用的字節(jié)例证,則返回一個錯誤.
func (b *Reader) ReadByte() (byte, error) {}
// ReadBytes路呜,讀取直到第一次出現(xiàn)分隔符,返回讀取到的字節(jié)切片织咧。
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {}
// ReadString 功能同 ReadBytes胀葱,只不過返回的是字符串。
func (b *Reader) ReadString(delim byte) (string, error) {}
示例代碼:
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
filePath := "aa.txt"
// 1.打開文件
f, err := os.Open(filePath)
if err != nil {
fmt.Println("err: ", err)
return
}
// 2.延遲關(guān)閉文件
defer f.Close()
// 3.讀取內(nèi)容
r := bufio.NewReader(f)
for {
s, err := r.ReadString('\n')
if err == io.EOF {
// 判斷s中是否有內(nèi)容需要輸出笙蒙。
if len(s) != 0 {
fmt.Println(s)
}
fmt.Println("讀取完畢")
break
}
if err != nil && err != io.EOF {
fmt.Println("err: ", err)
return
}
fmt.Println(s)
}
}
輸出結(jié)果
兩岸舟船各背馳抵屿,
波痕交涉亦難為。
只余鷗鷺無拘管手趣,
北去南來自在飛晌该。
讀取完畢
- bufio.Writer:
// NewWriterSize 將 wr 封裝成一個帶緩存的 bufio.Writer 對象肥荔,
// 緩存大小由 size 指定(如果小于 4096 則會被設(shè)置為 4096)绿渣。
// 如果 wr 的基類型就是有足夠緩存的 bufio.Writer 類型,則直接將
// wr 轉(zhuǎn)換為基類型返回燕耿。
func NewWriterSize(wr io.Writer, size int) *Writer{}
// NewWriter 相當(dāng)于 NewWriterSize(wr, 4096)
func NewWriter(wr io.Writer) *Writer{}
// WriteString 功能同 Write中符,只不過寫入的是字符串
func (b *Writer) WriteString(s string) (int, error){}
// Flush 將緩存中的數(shù)據(jù)提交到底層的 io.Writer 中
func (b *Writer) Flush() error{}
示例代碼:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
filePath := "ab.txt"
// 1.打開文件
f, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.ModePerm)
if err != nil {
fmt.Println("err: ", err)
return
}
// 2.延遲關(guān)閉文件
defer f.Close()
// 3.寫入內(nèi)容
w := bufio.NewWriter(f)
n, err := w.WriteString("hello world")
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Printf("寫入了%d個字節(jié)\n", n)
}
運行結(jié)果
寫入了11個字節(jié)
四、ioutil包
除了io包可以讀寫數(shù)據(jù)誉帅,Go語言中還提供了一個輔助的工具包就是ioutil淀散,里面的方法雖然不多,但是都還蠻好用的蚜锨。
該包的介紹只有一句話:Package ioutil implements some I/O utility functions档插。
ioutil包的方法:
// ReadAll 讀取 r 中的所有數(shù)據(jù),返回讀取的數(shù)據(jù)和遇到的錯誤亚再。
// 如果讀取成功郭膛,則 err 返回 nil,而不是 EOF氛悬,因為 ReadAll 定義為讀取
// 所有數(shù)據(jù)则剃,所以不會把 EOF 當(dāng)做錯誤處理。
func ReadAll(r io.Reader) ([]byte, error){}
// ReadFile 讀取文件中的所有數(shù)據(jù)如捅,返回讀取的數(shù)據(jù)和遇到的錯誤棍现。
// 如果讀取成功,則 err 返回 nil镜遣,而不是 EOF
func ReadFile(filename string) ([]byte, error){}
// WriteFile 向文件中寫入數(shù)據(jù)己肮,寫入前會清空文件。
// 如果文件不存在,則會以指定的權(quán)限創(chuàng)建該文件谎僻。
// 返回遇到的錯誤窖剑。
func WriteFile(filename string, data []byte, perm os.FileMode) error{}
// ReadDir 讀取指定目錄中的所有目錄和文件(不包括子目錄)。
// 返回讀取到的文件信息列表和遇到的錯誤戈稿,列表是經(jīng)過排序的西土。
func ReadDir(dirname string) ([]os.FileInfo, error){}
// TempFile 在 dir 目錄中創(chuàng)建一個以 prefix 為前綴的臨時文件,并將其以讀
// 寫模式打開鞍盗。返回創(chuàng)建的文件對象和遇到的錯誤需了。
// 如果 dir 為空,則在默認(rèn)的臨時目錄中創(chuàng)建文件(參見 os.TempDir)般甲,多次
// 調(diào)用會創(chuàng)建不同的臨時文件肋乍,調(diào)用者可以通過 f.Name() 獲取文件的完整路徑。
// 調(diào)用本函數(shù)所創(chuàng)建的臨時文件敷存,應(yīng)該由調(diào)用者自己刪除墓造。
func TempFile(dir, prefix string) (f *os.File, err error){}
// TempDir 功能同 TempFile,只不過創(chuàng)建的是目錄锚烦,返回目錄的完整路徑觅闽。
func TempDir(dir, prefix string) (name string, err error){}
示例代碼:
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func main() {
filePath := "aa.txt"
// 1.ReadFile
data, err := ioutil.ReadFile(filePath)
handleError(err)
fmt.Println(string(data))
// 2.ReadAll
s := "相濡以沫,不如相忘于江湖"
r := strings.NewReader(s)
data, err = ioutil.ReadAll(r)
handleError(err)
fmt.Println(string(data))
// 3.ReadDir
dirName := "E:/golang/go_project"
fileInfoData, err := ioutil.ReadDir(dirName)
handleError(err)
for k, v := range fileInfoData {
fmt.Printf("第%d個文件名稱是%s, 是目錄嗎涮俄?%v\n", k, v.Name(), v.IsDir())
}
// 4.WriteFile
filePath = "cc.txt"
err = ioutil.WriteFile(filePath, []byte("相見亦難別亦難蛉拙,東風(fēng)無力百花殘"), os.ModePerm)
handleError(err)
// 5.TempFile
f, err := ioutil.TempFile(dirName, "test")
handleError(err)
fmt.Println(f)
n, err := f.WriteString("這是一種的臨時測試文件")
handleError(err)
fmt.Println("寫入字符個數(shù):", n)
// 6.TempDir
name, err := ioutil.TempDir(dirName, "test")
handleError(err)
fmt.Println(name)
}
func handleError(err error) {
if err != nil {
fmt.Println("err: ", err)
return
}
}
運行結(jié)果
兩岸舟船各背馳,
波痕交涉亦難為彻亲。
只余鷗鷺無拘管孕锄,
北去南來自在飛。
相濡以沫苞尝,不如相忘于江湖
第0個文件名稱是aa.txt, 是目錄嗎畸肆?false
第1個文件名稱是ab.txt, 是目錄嗎?false
第2個文件名稱是bb.txt, 是目錄嗎宙址?false
第3個文件名稱是cc.txt, 是目錄嗎轴脐?false
第4個文件名稱是go.mod, 是目錄嗎?false
第5個文件名稱是main, 是目錄嗎曼氛?true
第6個文件名稱是main.go, 是目錄嗎豁辉?false
第7個文件名稱是model, 是目錄嗎?true
第8個文件名稱是test523723675, 是目錄嗎舀患?false
&{0xc00007b180}
寫入字符個數(shù): 33
E:\golang\go_project\test800637258
五徽级、文件復(fù)制
- 第一種:io包下Read()和Write()直接讀寫:我們自己創(chuàng)建讀取數(shù)據(jù)的切片的大小,直接影響性能聊浅。
- 第二種:ioutil包
- 第三種:io包下Copy()方法:
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
)
func main() {
srcFile := "aa.txt"
distFile := "bb.txt"
// n, err := copyFile1(srcFile, distFile)
// n, err := copyFile2(srcFile, distFile)
n, err := copyFile3(srcFile, distFile)
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Println("拷貝的字節(jié)數(shù):", n)
}
// copyFile1 io包的Read和Write方法
func copyFile1(srcFile, distFile string) (n int, err error) {
srcfile, err := os.Open(srcFile)
if err != nil {
return 0, err
}
distfile, err := os.OpenFile(distFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.ModePerm)
if err != nil {
return 0, err
}
defer srcfile.Close()
defer distfile.Close()
total := 0
temp := make([]byte, 1024)
for {
n, err = srcfile.Read(temp)
if err == io.EOF || n == 0 {
fmt.Println("拷貝完畢")
break
} else if err != nil && err != io.EOF {
return total, err
}
total += n
_, err = distfile.Write(temp[:n])
}
return total, nil
}
// copyFile2 ioutil包的方法
func copyFile2(srcFile, distFile string) (n int, err error) {
data, err := ioutil.ReadFile(srcFile)
if err != nil {
return 0, err
}
err = ioutil.WriteFile(distFile, data, os.ModePerm)
if err != nil {
return 0, err
}
return len(data), nil
}
// copyFile3 io包的copy方法
func copyFile3(srcFile, distFile string) (n int, err error) {
srcfile, err := os.Open(srcFile)
if err != nil {
return 0, err
}
distfile, err := os.OpenFile(distFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.ModePerm)
if err != nil {
return 0, err
}
defer srcfile.Close()
defer distfile.Close()
total, err := io.Copy(distfile, srcfile)
return int(total), err
}
六餐抢、斷點續(xù)傳
1现使、Seeker接口
Seeker是包裝基本Seek方法的接口。
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
seek(offset, whence)旷痕,設(shè)置指針光標(biāo)的位置碳锈,隨機(jī)讀寫文件:
第一個參數(shù):偏移量
第二個參數(shù):如何設(shè)置
0:seekStart表示相對于文件開始,
1:seekCurrent表示相對于當(dāng)前偏移量欺抗,
2:seek end表示相對于結(jié)束售碳。
const (
SEEK_SET int = 0 // seek relative to the origin of the file
SEEK_CUR int = 1 // seek relative to the current offset
SEEK_END int = 2 // seek relative to the end
)
示例代碼:
package main
import (
"fmt"
"os"
)
func main() {
filePath := "aa.txt" // ABCDEFGH
temp := make([]byte, 1)
f, _ := os.Open(filePath)
f.Seek(2, os.SEEK_SET)
f.Read(temp)
fmt.Println(string(temp[:])) // C
}
2、斷點續(xù)傳實現(xiàn)
首先思考幾個問題
Q1:如果你要傳的文件比較大绞呈,那么是否有方法可以縮短耗時贸人?
Q2:如果在文件傳遞過程中,程序因各種原因被迫中斷了佃声,那么下次再重啟時艺智,文件是否還需要重頭開始?
Q3:傳遞文件的時候圾亏,支持暫停和恢復(fù)么十拣?即使這兩個操作分布在程序進(jìn)程被殺前后。
通過斷點續(xù)傳可以實現(xiàn)志鹃,不同的語言有不同的實現(xiàn)方式夭问。我們看看Go語言中,通過Seek()方法如何實現(xiàn):
先說一下思路:想實現(xiàn)斷點續(xù)傳弄跌,主要就是記住上一次已經(jīng)傳遞了多少數(shù)據(jù)甲喝,那我們可以創(chuàng)建一個臨時文件,記錄已經(jīng)傳遞的數(shù)據(jù)量铛只,當(dāng)恢復(fù)傳遞的時候,先從臨時文件中讀取上次已經(jīng)傳遞的數(shù)據(jù)量糠溜,然后通過Seek()方法淳玩,設(shè)置到該讀和該寫的位置,再繼續(xù)傳遞數(shù)據(jù)非竿。
示例代碼:
package main
import (
"fmt"
"io"
"os"
"strconv"
)
func main() {
srcFile := "06.jpg"
distFile := "美女.jpg"
tempFile := "temp.txt"
tempfile, err := os.OpenFile(tempFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
handleError(err)
srcfile, err := os.Open(srcFile)
handleError(err)
distfile, err := os.OpenFile(distFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
handleError(err)
defer srcfile.Close()
defer distfile.Close()
// defer tempfile.Close()
// 1.讀取臨時文件夾的內(nèi)容蜕着,獲得已拷貝的字節(jié)數(shù)
_, err = tempfile.Seek(0, io.SeekStart)
handleError(err)
temp := make([]byte, 1024*4)
n1, err := tempfile.Read(temp)
handleError(err)
count, err := strconv.Atoi(string(temp[:n1]))
handleError(err)
// 2.設(shè)置讀寫偏移量
_, err = srcfile.Seek(int64(count), os.SEEK_SET)
handleError(err)
_, err = distfile.Seek(int64(count), os.SEEK_SET)
handleError(err)
data := make([]byte, 1024*4)
total := int(count)
// 3.循環(huán)讀寫數(shù)據(jù)
for {
n2, err := srcfile.Read(data)
if n2 == 0 || err == io.EOF {
fmt.Println("文件讀完了")
tempfile.Close()
os.Remove(tempFile)
break
}
// 寫入數(shù)據(jù)
n3, err := distfile.Write(data[:n2])
// 將寫入量寫入臨時文件中
total += n3
_, err = tempfile.Seek(0, os.SEEK_SET)
handleError(err)
_, err = tempfile.WriteString(strconv.Itoa(total))
handleError(err)
// 模擬程序中斷
if total > 2000 {
panic("崩了")
}
}
}
func handleError(err error) {
if err != nil {
fmt.Println("err: ", err)
return
}
}
運行結(jié)果
運行5次程序?qū)崿F(xiàn)圖片的拷貝