700 lines
17 KiB
Markdown
700 lines
17 KiB
Markdown
# 计算机 I/O 与文件系统基础
|
||
|
||
## 数据的本质
|
||
|
||
计算机中的所有数据本质上都是二进制流,即由 0 和 1 组成的序列。不同类型的文件(如图片、视频、文本等)只是这些二进制数据按照特定格式编码和解码的结果。例如:
|
||
|
||
- 图片文件(JPG):二进制数据按照 JPEG 格式编码,显示器将其解析为可视化的图像
|
||
- 视频文件(MP4):二进制数据按照 MP4 编码规范存储,包含视频帧、音频等信息
|
||
- 文本文件:二进制数据通过字符编码(如 UTF-8)转换为可读的文字
|
||
|
||
## I/O 的含义
|
||
|
||
I/O(输入/输出)是计算机与外部世界交换数据的基础:
|
||
|
||
- Input(输入):从外部获取数据到计算机系统
|
||
- Output(输出):将数据从计算机系统传输到外部
|
||
|
||
## 文件系统中的文件类型
|
||
|
||
主要分为两大类:
|
||
|
||
1. 文本文件:存储人类可读的字符序列
|
||
2. 二进制文件:存储原始的二进制数据(如图片、视频、可执行文件等)
|
||
|
||
## 文件操作的基本功能
|
||
|
||
现代编程语言通常通过封装底层操作系统的文件 API 来提供文件操作能力。核心功能包括:
|
||
|
||
1. 获取文件信息(FileInfo)
|
||
- 文件大小
|
||
- 创建时间
|
||
- 修改时间
|
||
- 访问权限等
|
||
|
||
2. 文件读取(Read)
|
||
- 读取文本内容
|
||
- 读取二进制数据
|
||
- 支持随机访问和顺序读取
|
||
|
||
3. 文件写入(Write)
|
||
- 写入文本内容
|
||
- 写入二进制数据
|
||
- 支持追加和覆盖模式
|
||
|
||
这种组织方式更系统地展示了这些概念之间的关系,并提供了更详细的解释。你觉得这样的组织方式如何?是否还需要在某些方面做进一步的调整?
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
// file
|
||
// fileInfo
|
||
/*
|
||
type FileInfo interface {
|
||
Name() string // base name of the file
|
||
Size() int64 // length in bytes for regular files; system-dependent for others
|
||
Mode() FileMode // file mode bits : 权限
|
||
ModTime() time.Time // modification time
|
||
IsDir() bool // abbreviation for Mode().IsDir()
|
||
|
||
// 获取更加详细的文件信息, *syscall.Stat_t 反射来获取
|
||
Sys() any // underlying data source (can return nil)
|
||
*/
|
||
func main() {
|
||
// 获取某个文件的状态
|
||
fileinfo, err := os.Stat("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\test")
|
||
if err != nil {
|
||
return
|
||
}
|
||
fmt.Println(fileinfo.Name()) // demo01.go
|
||
fmt.Println(fileinfo.IsDir()) // false
|
||
fmt.Println(fileinfo.ModTime()) // 2023-02-23 20:25:43.1772351 +0800 CST
|
||
fmt.Println(fileinfo.Size()) // 1186 字节数
|
||
fmt.Println(fileinfo.Mode()) // -rw-rw-rw-
|
||
}
|
||
```
|
||
|
||
## 创建文件、目录
|
||
|
||
通过代码创建文件
|
||
|
||
路径:
|
||
|
||
- 相对路径
|
||
- 相对当前目录的路径
|
||
- ./ 当前目录
|
||
- ../ 上一级目录
|
||
- 绝对路径
|
||
- 从盘符开始的路径
|
||
|
||
> 创建目录
|
||
|
||
mkdir / 权限
|
||
|
||
mkdirAll 创建层级目录
|
||
|
||
remove 删除目录
|
||
|
||
removeAll 强制删除目录
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
// 创建目录
|
||
// 项目开源框架,一运行,就会自动生成脚手架目录
|
||
func main() {
|
||
|
||
// 打开一个文件夹(1、存在我就打开 2、不存在,创建这个文件夹)
|
||
// func Mkdir(name string, perm FileMode) error
|
||
// ModePerm : 0777
|
||
err := os.Mkdir("D:\\MyGo\\src\\github.com\\Docker7530\\testproject\\file1", os.ModePerm)
|
||
if err != nil {
|
||
// 存在就无法创建了 Cannot create a file when that file already exists.
|
||
fmt.Println(err)
|
||
}
|
||
fmt.Println("文件夹创建完毕")
|
||
|
||
// 创建层级文件夹
|
||
err2 := os.MkdirAll("D:\\MyGo\\src\\github.com\\Docker7530\\testproject\\file2\\aa\\bb\\cc\\dd", os.ModePerm)
|
||
if err2 != nil {
|
||
fmt.Println(err2)
|
||
}
|
||
fmt.Println("层级文件夹创建完毕")
|
||
|
||
// 删除 remove
|
||
// func Remove(name string) error
|
||
// 通过remove方法只能删除单个空文件夹:
|
||
// remove D:\Environment\GoWorks\src\xuego\lesson11\file2: The directory is not empty.
|
||
err3 := os.Remove("D:\\MyGo\\src\\github.com\\Docker7530\\testproject\\file2")
|
||
if err3 != nil {
|
||
fmt.Println(err3)
|
||
//return
|
||
}
|
||
fmt.Println("file delete success!!")
|
||
// 如果存在多层文件,removeAll,相对来说比较危险,删除这个目录下的所有东西, 强制删除
|
||
err4 := os.RemoveAll("D:\\MyGo\\src\\github.com\\Docker7530\\testproject\\file2")
|
||
if err4 != nil {
|
||
fmt.Println(err4)
|
||
return
|
||
}
|
||
fmt.Println("err4 delete success!!")
|
||
}
|
||
|
||
```
|
||
|
||
> 创建文件
|
||
|
||
os.create(),若存在就是的打开-就是返回的这个file对象. 如果不存在,创建在打开
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
func main() {
|
||
// 创建文件 Create
|
||
// func Create(name string) (*File, error) {
|
||
// 返回的file对象就是我们的文件
|
||
file1, err := os.Create("a.go") // 相对路径
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
}
|
||
fmt.Println(file1)
|
||
// 删除
|
||
os.Remove("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\a.go")
|
||
|
||
}
|
||
|
||
```
|
||
|
||
## IO 读
|
||
|
||
1、与文件建立连接
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
// IO读
|
||
func main() {
|
||
|
||
// 找到这个文件的对象 create 创建、 打开Open
|
||
// func Open(name string) (*File, error)
|
||
file1, err := os.Open("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\狂神的Go世界.txt")
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
}
|
||
fmt.Println(file1)
|
||
// file 类-- 指定的对象
|
||
// 打开文件的时候,选定权限: 可读可写的方式打开
|
||
// OpenFile(文件名,打开方式:可读、可写...,FileMode , 权限)
|
||
file2, err2 := os.OpenFile("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\狂神的Go世界.txt",
|
||
os.O_RDONLY|os.O_WRONLY, os.ModePerm)
|
||
if err2 != nil {
|
||
fmt.Println(err2)
|
||
}
|
||
fmt.Println(file2)
|
||
|
||
// 可以操作这个对象了
|
||
|
||
}
|
||
|
||
```
|
||
|
||
2、读取 file.Read([]byte) ,将file中的数据读取到 []byte 中, n,err n读取到的行数,err 错误,EOF错误,就代表文件读取完毕了
|
||
|
||
一直调用read,就代表光标往后移动….
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
// 读取文件数据
|
||
func main() {
|
||
// 我们习惯于在建立连接时候通过defer来关闭连接,保证程序不会出任何问题,或者忘记关闭
|
||
// 建立连接
|
||
file, _ := os.Open("狂神的Go世界.txt")
|
||
// 关闭连接
|
||
defer file.Close()
|
||
|
||
// 读代码 ,Go 的错误机制,让我们专心可以写业务代码。
|
||
|
||
// 1、创建一个容器 (二进制文本文件--0100101010 => 读取流到一个容器 => 读取容器的数据)
|
||
bs := make([]byte, 2, 1024) // 缓冲区,可以接受我们读取的数据
|
||
// 2、读取到缓冲区中。 // 汉字一个汉字占 3个位置
|
||
n, err := file.Read(bs)
|
||
fmt.Println(n)
|
||
fmt.Println(err)
|
||
fmt.Println(string(bs)) // 读取到的字符串 ab
|
||
// 光标不停的向下去指向,读取出来的内容就存到我们的容器中。
|
||
file.Read(bs)
|
||
fmt.Println(string(bs)) // 读取到的字符串 cd
|
||
file.Read(bs)
|
||
fmt.Println(string(bs)) // 读取到的字符串 e
|
||
n, err = file.Read(bs)
|
||
fmt.Println(n)
|
||
fmt.Println(err) // EOF ,读取到了文件末尾。就会返回EOF。
|
||
fmt.Println(string(bs)) // 读取到的字符串
|
||
n, err = file.Read(bs)
|
||
fmt.Println(n)
|
||
fmt.Println(err)
|
||
fmt.Println(string(bs)) // 读取到的字符串
|
||
|
||
}
|
||
```
|
||
|
||
## IO 写(权限)
|
||
|
||
建立连接 (设置权限:可读可写,扩充这个文件的append) os.OpenFile
|
||
|
||
关闭连接
|
||
|
||
写入 file.write
|
||
|
||
- file.Write
|
||
- file.WriteString
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
func main() {
|
||
|
||
fileName := "狂神的Go世界.txt"
|
||
// 权限:如果我们要向一个文件中追加内容, O_APPEND, 如果没有,就是从头开始写
|
||
file, _ := os.OpenFile(fileName, os.O_WRONLY|os.O_RDONLY|os.O_APPEND, os.ModePerm)
|
||
defer file.Close()
|
||
|
||
// 操作
|
||
bs := []byte{65, 66, 67, 68, 69} // A B C D E
|
||
n, err := file.Write(bs)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
}
|
||
fmt.Println(n)
|
||
|
||
// string类型的写入
|
||
n, err = file.WriteString("hhahahahah哈哈哈哈哈哈哈")
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
}
|
||
fmt.Println(n)
|
||
}
|
||
|
||
```
|
||
|
||
## 文件复制
|
||
|
||
```go
|
||
package utils
|
||
|
||
import (
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
)
|
||
|
||
// Copy 方法需要参数为:source 源文件 ,destination 目标文件
|
||
func Copy(source, destination string, bufferSize int) {
|
||
|
||
// 读取文件
|
||
sourceFile, err := os.Open(source)
|
||
if err != nil {
|
||
fmt.Println("Open错误:", err)
|
||
}
|
||
// 输出文件 O_WRONLY , O_CREATE 如果不不存在,则会创建
|
||
destinationFile, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
||
if err != nil {
|
||
fmt.Println("OpenFile错误:", err)
|
||
}
|
||
// 关闭
|
||
defer sourceFile.Close()
|
||
defer destinationFile.Close()
|
||
|
||
// 专注业务代码,拷贝
|
||
buf := make([]byte, bufferSize)
|
||
// 读取
|
||
for {
|
||
n, err := sourceFile.Read(buf)
|
||
if n == 0 || err == io.EOF {
|
||
fmt.Println("读取完毕源文件,复制完毕")
|
||
break
|
||
} else if err != nil {
|
||
fmt.Println("读取错误:", err)
|
||
return // 错误之后,必须要return终止函数执行。
|
||
}
|
||
// 将缓冲区的东西写出到目标文件
|
||
_, err = destinationFile.Write(buf[:n])
|
||
if err != nil {
|
||
fmt.Println("写出错误:", err)
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
```
|
||
|
||
调用
|
||
|
||
```go
|
||
package main
|
||
|
||
import "xuego/lesson11/utils"
|
||
|
||
func main() {
|
||
source := "C:\\Users\\遇见狂神说\\Desktop\\xq.png"
|
||
dest := "D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\xq.png"
|
||
|
||
utils.Copy(source, dest, 1024)
|
||
|
||
}
|
||
```
|
||
|
||
> 系统给我们提供了copy方法
|
||
|
||
```go
|
||
// 调用系统的方法
|
||
func Copy2(source, destination string) {
|
||
// 读取文件
|
||
sourceFile, err := os.Open(source)
|
||
if err != nil {
|
||
fmt.Println("Open错误:", err)
|
||
}
|
||
// 输出文件 O_WRONLY , O_CREATE 如果不不存在,则会创建
|
||
destinationFile, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
||
if err != nil {
|
||
fmt.Println("OpenFile错误:", err)
|
||
}
|
||
// 关闭
|
||
defer sourceFile.Close()
|
||
defer destinationFile.Close()
|
||
|
||
// 具体的实现
|
||
written, err := io.Copy(destinationFile, sourceFile)
|
||
fmt.Println("文件的字节大小:", written)
|
||
}
|
||
```
|
||
|
||
## Seeker接口
|
||
|
||
设置光标的位置,读写文件
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
)
|
||
|
||
func main() {
|
||
// 1. 创建一个测试文件
|
||
file, err := os.OpenFile("狂神的Go世界1.txt", os.O_RDWR|os.O_CREATE, 0666)
|
||
if err != nil {
|
||
fmt.Println("打开文件错误:", err)
|
||
return
|
||
}
|
||
defer file.Close()
|
||
|
||
// 2. 写入初始内容
|
||
file.WriteString("Hello World!")
|
||
|
||
// 3. 演示不同的 Seek 操作
|
||
demonstrateSeek(file)
|
||
}
|
||
|
||
func demonstrateSeek(file *os.File) {
|
||
// 重置光标到文件开始
|
||
file.Seek(0, io.SeekStart)
|
||
|
||
// 从文件开始读取 5 个字节
|
||
buf := make([]byte, 5)
|
||
n, err := file.Read(buf)
|
||
if err != nil {
|
||
fmt.Println("读取错误:", err)
|
||
return
|
||
}
|
||
fmt.Printf("1. 读取前5个字符: %s\n", string(buf[:n]))
|
||
|
||
// 从当前位置向后移动 1 个字节
|
||
file.Seek(1, io.SeekCurrent)
|
||
buf = make([]byte, 5)
|
||
n, _ = file.Read(buf)
|
||
fmt.Printf("2. 跳过1个字符后读取: %s\n", string(buf[:n]))
|
||
|
||
// 从文件末尾向前移动 5 个字节
|
||
file.Seek(-5, io.SeekEnd)
|
||
buf = make([]byte, 5)
|
||
n, _ = file.Read(buf)
|
||
fmt.Printf("3. 从末尾往前5个字符读取: %s\n", string(buf[:n]))
|
||
|
||
// 在文件末尾追加内容
|
||
file.Seek(0, io.SeekEnd)
|
||
file.WriteString("\nAppended Text")
|
||
|
||
// 读取整个文件内容
|
||
file.Seek(0, io.SeekStart)
|
||
content, _ := io.ReadAll(file)
|
||
fmt.Printf("\n4. 最终文件内容:\n%s\n", string(content))
|
||
}
|
||
|
||
```
|
||
|
||
## 断点续传
|
||
|
||
思考几个问题:
|
||
|
||
1、如果你要传的文件很大,70G,是否有方法可以缩短耗时?
|
||
|
||
- 将文件拆分
|
||
- 同时多线程进行下载
|
||
|
||
2、如果在文件传递过程中,程序被迫中断(断电、断网、内存满了..),下次重启之后,文件是否还需要重头再传?
|
||
|
||
- 希望能够继续上传或者下载
|
||
|
||
3、传递文件的时候,支持暂停和恢复上传?假设这个两个操作分布在重启前后?
|
||
|
||
- 支持!
|
||
|
||
file、read、write、seek
|
||
|
||
思路:
|
||
|
||
1、需要记住上一次传递了多少数据、temp.txt => 记录
|
||
|
||
2、如果被暂停或者中断了,我们就可以读取这个temp.txt的记录,恢复上传
|
||
|
||
3、删除temp.txt
|
||
|
||

|
||
|
||
**所有人必须要要全部理解的一段代码**
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
"strconv"
|
||
)
|
||
|
||
// 断点续传
|
||
func main() {
|
||
|
||
// 传输源文件地址
|
||
srcFile := "C:\\Users\\遇见狂神说\\Desktop\\client\\gp.png"
|
||
// 传输的目标位置
|
||
destFile := "D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\server\\gp-upload.png"
|
||
// 临时记录文件
|
||
tempFile := "D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\temp.txt"
|
||
|
||
// 创建对应的file对象,连接起来
|
||
file1, _ := os.Open(srcFile)
|
||
file2, _ := os.OpenFile(destFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
|
||
file3, _ := os.OpenFile(tempFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
|
||
defer file1.Close()
|
||
defer file2.Close()
|
||
fmt.Println("file1/2/3 文件连接建立完毕")
|
||
|
||
// 1、读取temp.txt
|
||
file3.Seek(0, io.SeekStart)
|
||
buf := make([]byte, 1024, 1024)
|
||
n, _ := file3.Read(buf)
|
||
// 2、转换成string - 数字。
|
||
countStr := string(buf[:n])
|
||
count, _ := strconv.ParseInt(countStr, 10, 64)
|
||
fmt.Println("temp.txt中记录的值为:", count) // 5120
|
||
|
||
// 3、设置读写的偏移量
|
||
file1.Seek(count, io.SeekStart)
|
||
file2.Seek(count, io.SeekStart)
|
||
fmt.Println("file1/2 光标已经移动到了目标位置")
|
||
|
||
// 4、开始读写(复制、上传)
|
||
bufData := make([]byte, 1024, 1024)
|
||
// 5、需要记录读取了多少个字节
|
||
total := int(count)
|
||
|
||
for {
|
||
// 读取数据
|
||
readNum, err := file1.Read(bufData)
|
||
if err == io.EOF { // file1 读取完毕了
|
||
fmt.Println("文件传输完毕了")
|
||
file3.Close()
|
||
os.Remove(tempFile)
|
||
break
|
||
}
|
||
|
||
// 向目标文件中写入数据
|
||
writeNum, err := file2.Write(bufData[:readNum])
|
||
|
||
// 将写入数据放到 total中, 在这里total 就是传输的进度
|
||
total = total + writeNum
|
||
// temp.txt 存放临时记录数据
|
||
file3.Seek(0, io.SeekStart) // 将光标重置到开头
|
||
file3.WriteString(strconv.Itoa(total))
|
||
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
## 遍历文件夹
|
||
|
||
**下去一定要自己实现**
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
)
|
||
|
||
// cd /d 文件夹路径
|
||
// tree /F , 查看当前文件夹下的所有文件
|
||
|
||
// 遍历文件夹
|
||
// 1、读取当前文件夹下的所有文件
|
||
// 2、如果是文件夹,进入文件夹,继续读取里面的所有文件
|
||
// 3、设置一些结构化代码
|
||
func main() {
|
||
dir := "D:\\Environment\\GoWorks\\src\\xuego"
|
||
tree(dir, 0)
|
||
}
|
||
|
||
// 日常调试测试常用fmt输出 、 工作中or项目中更多是log日志输出
|
||
func tree(dir string, level int) {
|
||
// 编写层级
|
||
tabString := "|--"
|
||
for i := 0; i < level; i++ {
|
||
tabString = "| " + tabString
|
||
}
|
||
|
||
// 获取目录 ReadDir, 返回目录信息[]DirEntry,多个文件信息
|
||
fileInfos, err := os.ReadDir(dir)
|
||
if err != nil {
|
||
log.Println(err)
|
||
}
|
||
|
||
// 遍历出来所有文件之后,获取里面的单个文件
|
||
for _, file := range fileInfos {
|
||
// 文件夹中文件的全路径展示
|
||
filename := dir + "\\" + file.Name()
|
||
fmt.Println(tabString + file.Name())
|
||
// 如果是文件夹,再次遍历
|
||
if file.IsDir() {
|
||
tree(filename, level+1)
|
||
}
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
## bufio
|
||
|
||
Go语言自带的IO操作包。bufio,使用这个包可以大幅提升文件的读写效率。
|
||
|
||
buf: 缓冲区.
|
||
|
||
io操作效率本身是还可以的,频繁访问本地磁盘文件(效率低)
|
||
|
||
所以说 bufio ,提供了一个缓冲区,读和写都先在缓冲区中,最后再一次性读取或者写入到文件里,降低访问本地磁盘的次数。
|
||
|
||

|
||
|
||
bufio写入
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
)
|
||
|
||
// bufio 的应用
|
||
func main() {
|
||
file, err := os.Open("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\demo01.go")
|
||
if err != nil {
|
||
log.Println(err)
|
||
}
|
||
defer file.Close()
|
||
|
||
// 读取文件
|
||
// 创建一个bufio包下的 reader对象。
|
||
//bufioReader := bufio.NewReader(file)
|
||
//buf := make([]byte, 1024)
|
||
//n, err := bufioReader.Read(buf)
|
||
//fmt.Println("读取到了多少个字节:", n)
|
||
|
||
// 读取键盘的输入
|
||
// 键盘的输入,实际上是流 os.Stdin
|
||
inputReader := bufio.NewReader(os.Stdin)
|
||
// delim 到哪里结束读取
|
||
readString, _ := inputReader.ReadString('\n')
|
||
fmt.Println("读取键盘输入的信息:", readString)
|
||
|
||
}
|
||
```
|
||
|
||
bufio写出
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
// 写入
|
||
func main() {
|
||
file, _ := os.OpenFile("D:\\Environment\\GoWorks\\src\\xuego\\lesson11\\a.txt",
|
||
os.O_RDWR|os.O_CREATE,
|
||
os.ModePerm)
|
||
defer file.Close()
|
||
|
||
// bufio
|
||
fileWrite := bufio.NewWriter(file)
|
||
writeNum, _ := fileWrite.WriteString("kuangshen")
|
||
fmt.Println("writeNum:", writeNum)
|
||
// 发现并没有写出到文件,是留在了缓冲区,所以我们需要调用 flush 刷新缓冲区
|
||
// 手动刷新进文件
|
||
fileWrite.Flush()
|
||
}
|
||
```
|