【go語言學(xué)習(xí)】文件操作file

一娘扩、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)容况鸣,思路如下:

  1. 當(dāng)緩存區(qū)有內(nèi)容的時薄风,將緩存區(qū)內(nèi)容全部填入p并清空緩存區(qū)
  2. 當(dāng)緩存區(qū)沒有內(nèi)容的時候且len(p)>len(buf),即要讀取的內(nèi)容比緩存區(qū)還要大饵较,直接去文件讀取即可
  3. 當(dāng)緩存區(qū)沒有內(nèi)容的時候且len(p)<len(buf),即要讀取的內(nèi)容比緩存區(qū)小,緩存區(qū)從文件讀取內(nèi)容充滿緩存區(qū)遭赂,并將p填滿(此時緩存區(qū)有剩余內(nèi)容)
  4. 以后再次讀取時緩存區(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) 的思路如下

  1. 判斷buf中可用容量是否可以放下 p
  2. 如果能放下,直接把p拼接到buf后面撇他,即把內(nèi)容放到緩沖區(qū)
  3. 如果緩沖區(qū)的可用容量不足以放下茄猫,且此時緩沖區(qū)是空的,直接把p寫入文件即可
  4. 如果緩沖區(qū)的可用容量不足以放下逆粹,且此時緩沖區(qū)有內(nèi)容募疮,則用p把緩沖區(qū)填滿,把緩沖區(qū)所有內(nèi)容寫入文件僻弹,并清空緩沖區(qū)
  5. 判斷p的剩余內(nèi)容大小能否放到緩沖區(qū)阿浓,如果能放下(此時和步驟1情況一樣)則把內(nèi)容放到緩沖區(qū)
  6. 如果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)圖片的拷貝
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市红柱,隨后出現(xiàn)的幾起案子承匣,更是在濱河造成了極大的恐慌,老刑警劉巖锤悄,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件韧骗,死亡現(xiàn)場離奇詭異,居然都是意外死亡零聚,警方通過查閱死者的電腦和手機(jī)袍暴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門些侍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人政模,你說我怎么就攤上這事岗宣。” “怎么了淋样?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵耗式,是天一觀的道長。 經(jīng)常有香客問我趁猴,道長纽什,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任躲叼,我火速辦了婚禮芦缰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枫慷。我一直安慰自己让蕾,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布或听。 她就那樣靜靜地躺著探孝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪誉裆。 梳的紋絲不亂的頭發(fā)上顿颅,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音足丢,去河邊找鬼粱腻。 笑死,一個胖子當(dāng)著我的面吹牛斩跌,可吹牛的內(nèi)容都是我干的绍些。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼耀鸦,長吁一口氣:“原來是場噩夢啊……” “哼柬批!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袖订,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤氮帐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洛姑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上沐,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年吏口,在試婚紗的時候發(fā)現(xiàn)自己被綠了奄容。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冰更。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昂勒,靈堂內(nèi)的尸體忽然破棺而出蜀细,到底是詐尸還是另有隱情,我是刑警寧澤戈盈,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布奠衔,位于F島的核電站,受9級特大地震影響塘娶,放射性物質(zhì)發(fā)生泄漏归斤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一刁岸、第九天 我趴在偏房一處隱蔽的房頂上張望脏里。 院中可真熱鬧,春花似錦虹曙、人聲如沸迫横。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽矾踱。三九已至,卻和暖如春疏哗,著一層夾襖步出監(jiān)牢的瞬間呛讲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工返奉, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留贝搁,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓衡瓶,卻偏偏與公主長得像徘公,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子哮针,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354