点击蓝字,关注我们
全文9792字,预计阅读时间10分钟。
01
以下对接口进行了源码摘取并进行中文注释:
/*
Reader 是包装基本 Read 方法的接口。
Read 将最多 len(p) 个字节读取到 p 中。它返回读取的字节数 (0 <= n <= len(p)) 以及遇到的任何错误。即使 Read 返回 n < len(p),它也可能在调用期间使用所有 p 作为暂存空间。如果某些数据可用,但不是 len(p) 个字节,则 Read 通常会返回可用数据,而不是等待更多数据。
当 Read 在成功读取 n > 0 字节后遇到错误或文件结束条件时,它返回读取的字节数。它可能会从同一调用返回(非零)错误,或从后续调用返回错误(且 n == 0)。这种一般情况的一个实例是,在输入流末尾返回非零字节数的 Reader 可能返回 err == EOF 或 err == nil。下一次读取应返回 0、EOF。
在考虑错误 err 之前,调用者应始终处理返回的 n > 0 字节。这样做可以正确处理读取一些字节后发生的 I/O 错误以及允许的 EOF 行为。
如果 len(p) == 0,Read 应始终返回 n == 0。如果已知某些错误条件(例如 EOF),则可能会返回非零错误。
不鼓励 Read 的实现返回带有 nil 错误的零字节计数,除非 len(p) == 0 时。调用者应将返回 0 和 nil 视为表示没有发生任何事情;特别是它不指示 EOF。
*/
type Writer interface {
Write(p []byte) (n int, err error)
}
/*
Writer 是包装基本 Write 方法的接口。
Write 将 p 中的 len(p) 个字节写入底层数据流。它返回从 p (0 <= n <= len(p)) 写入的字节数以及遇到的导致写入提前停止的任何错误。如果 Write 返回 n < len(p),则必须返回非零错误。写入不得修改切片数据,即使是暂时的。
*/
type Reader interface {
Read(p []byte) (n int, err error)
}
/*
Seeker 是包装基本 Seek 方法的接口。
Seek 将下一次读取或写入的偏移量设置为 offset,根据从何处解释:SeekStart表示相对于文件开头, SeekCurrent表示相对于当前偏移量, SeekEnd表示相对于结尾(例如,offset = - 2 指定文件的倒数第二个字节)。Seek 返回相对于文件开头的新偏移量或错误(如果有)。
寻找文件开始之前的偏移量是错误的。可以允许寻求任何正偏移量,但如果新偏移量超过底层对象的大小,则后续 I/O 操作的行为取决于实现。
*/
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
/*
Closer 是包装基本 Close 方法的接口。
第一次调用后 Close 的行为未定义。具体的实现可能会记录它们自己的行为。
*/
type Closer interface {
Close() error
}
以下对接口进行了源码摘取并进行中文注释:
/*
ReaderAt 是包装基本 ReadAt 方法的接口。
ReadAt 从底层输入源中的偏移量 off 处开始将 len(p) 个字节读取到 p 中。它返回读取的字节数 (0 <= n <= len(p)) 以及遇到的任何错误。
当 ReadAt 返回 n < len(p) 时,它返回一个非零错误,解释为什么没有返回更多字节。在这方面,ReadAt比Read更严格。
即使 ReadAt 返回 n < len(p),它也可能在调用期间使用所有 p 作为暂存空间。如果某些数据可用但不是 len(p) 个字节,则 ReadAt 会阻塞,直到所有数据可用或发生错误。在这方面,ReadAt 与 Read 不同。
如果 ReadAt 返回的 n = len(p) 字节位于输入源的末尾,则 ReadAt 可能返回 err == EOF 或 err == nil。
如果 ReadAt 正在从具有寻道偏移的输入源读取,则 ReadAt 不应影响底层寻道偏移,也不会受其影响。
ReadAt 的客户端可以在同一输入源上执行并行 ReadAt 调用。
*/
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
/*
RuneReader 是包装 ReadRune 方法的接口。
ReadRune 读取单个编码的 Unicode 字符并返回符文及其大小(以字节为单位)。如果没有可用的字符,则会设置 err。
*/
type RuneReader interface {
ReadRune() (r rune, size int, err error)
}
/*
ByteReader 是包装 ReadByte 方法的接口。
ReadByte 读取并返回输入中的下一个字节或遇到的任何错误。如果 ReadByte 返回错误,则表示没有消耗输入字节,并且返回的字节值未定义。
ReadByte 为逐字节处理提供了高效的接口。未实现 ByteReader 的 Reader可以使用 bufio.NewReader 进行包装以添加此方法。
*/
type ByteReader interface {
ReadByte() (byte, error)
}
/*
ReaderFrom 是包装 ReadFrom 方法的接口。
ReadFrom 从 r 读取数据,直到 EOF 或出现错误。返回值n是读取的字节数。读取期间遇到的除 EOF 之外的任何错误也会返回。
Copy函数使用ReaderFrom (如果可用)。
*/
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
/*
WriterAt 是包装基本 WriteAt 方法的接口。
WriteAt 将 p 中的 len(p) 个字节写入偏移量为 off 的基础数据流。它返回从 p (0 <= n <= len(p)) 写入的字节数以及遇到的导致写入提前停止的任何错误。如果 WriteAt 返回 n < len(p),则它必须返回非零错误。
如果 WriteAt 正在写入具有查找偏移量的目标,则 WriteAt 不应影响底层查找偏移量,也不会受其影响。
如果范围不重叠,WriteAt 的客户端可以在同一目标上执行并行 WriteAt 调用。
实现不得保留 p。
*/
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}
/*
WriterTo 是包装 WriteTo 方法的接口。
WriteTo 将数据写入 w,直到没有更多数据可写入或发生错误时。返回值n是写入的字节数。写入期间遇到的任何错误也会返回。
Copy 函数使用 WriterTo(如果可用)。
*/
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
/*
StringWriter 是包装 WriteString 方法的接口。
*/
type StringWriter interface {
WriteString(s string) (n int, err error)
}
Copy:
func Copy(dst Writer, src Reader) (written int64, err error)
成功的 Copy 返回 err == nil,而不是 err == EOF。因为 Copy 被定义为从 src 读取直到 EOF,所以它不会将 Read 中的 EOF 视为要报告的错误。
如果 src 实现WriterTo,则通过调用 src.WriteTo(dst) 实现复制。否则,如果 dst 实现了ReaderFrom,则通过调用 dst.ReadFrom(src) 来实现复制。
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
如果 src 实现WriterTo或 dst 实现ReaderFrom,则 buf 将不会用于执行复制。
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
func Pipe() (*PipeReader, *PipeWriter)
管道上的读取和写入是一对一匹配的,除非需要多个读取来消耗单个写入。也就是说,对 PipeWriter 的每次写入都会阻塞,直到满足来自PipeReader的一次或多次读取(完全消耗写入数据)为止。数据直接从Write复制到对应的Read(或Reads);没有内部缓冲。
并行调用 Read 和 Write 或与 Close 一起调用是安全的。对 Read 的并行调用和对 Write 的并行调用也是安全的:各个调用将按顺序进行门控。
func ReadAll(r Reader) ([]byte, error)
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
func ReadFull(r Reader, buf []byte) (n int, err error)
func WriteString(w Writer , s string ) (n int , err error)
// ReadFileExample 读取文件内容并输出
func ReadFileExample() {
// 打开文件,第一个参数是文件路径,第二个参数是文件打开模式
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close() // 延迟关闭文件,确保文件在函数执行完毕后被关闭
// 读取文件内容
data := make([]byte, 100) // 读取数据的缓冲区
count, err := file.Read(data)
if err != nil {
fmt.Println("Error:", err)
return
}
// 输出文件内容
fmt.Printf("Read %d bytes: %s\n", count, data[:count])
}
// WriteFileExample 函数演示如何写入数据到文件中
func WriteFileExample() {
// 创建文件,第一个参数是文件路径,如果文件已存在则会被截断清空
file, err := os.Create("example.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close() // 延迟关闭文件,确保文件在函数执行完毕后被关闭
// 写入数据到文件
data := []byte("Hello, world!\n")
_, err = file.Write(data)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Data has been written to output.txt")
}
/*
文件提供对单个文件的访问。File 接口是文件所需的最低实现。目录文件还应该实现ReadDirFile。文件可以实现io.ReaderAt或io.Seeker作为优化。
*/
type File interface {
Stat() (FileInfo, error)
Read([]byte) (int, error)
Close() error
}
/*
FileInfo 描述一个文件并由Stat返回。
*/
type FileInfo interface {
Name() string // 文件的基本名称
Size() int64 // 常规文件的长度(以字节为单位); 其他系统相关
Mode() FileMode // 文件模式位
ModTime() time . Time // 修改时间
IsDir() bool // Mode().IsDir() 的缩写
Sys() any // 底层数据源(可以返回 nil)
}
/*
FS 提供对分层文件系统的访问。
FS接口是文件系统所需的最低实现。文件系统可以实现附加接口,例如ReadFileFS,以提供附加或优化的功能。
testing/fstest.TestFS可用于测试 FS 实现的正确性。
*/
type FS interface {
// Open 打开指定的文件。
//
// 当 Open 返回错误时,错误类型应为 *PathError
// Op 字段设置为“open”,Path 字段设置为 name,
// 以及 Err 字段描述问题。
//
// Open 应拒绝打开不满足// ValidPath(name) 的名称的尝试,返回 *PathError,并将 Err 设置为
// ErrInvalid 或 ErrNotExist。
Open(name string) (File, error)
}
/*
DirEntry 是从目录读取的条目(使用ReadDir函数或ReadDirFile的 ReadDir 方法)。
*/
type DirEntry interface {
// Name 返回条目描述的文件(或子目录)的名称。
// 这个名称只是路径的最后一个元素(基本名称),而不是整个路径。
// 例如,Name 将返回“hello.go”而不是“home/gopher/hello.go”。
Name() string
// IsDir 报告条目是否描述目录
IsDir() bool
// Type 返回条目的类型位。
// 类型位是通常 FileMode 位的子集,由 FileMode.Type 方法返回
Type() FileMode
// Info 返回条目描述的文件或子目录的 FileInfo。
// 返回的 FileInfo 可能来自读取原始目录的时间
// 或来自调用 Info. //如果自目录读取后文件已被删除或重命名,Info 可能会返回满足errors.Is(err, ErrNotExist) 的错误。
// 如果条目表示符号链接,则 Info 报告有关链接本身的信息,
// 而不是链接目标的信息。
Info() (FileInfo, error)
}
02
// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo = fs.FileInfo
// A FileMode represents a file's mode and permission bits.
// The bits have the same definition on all systems, so that
// information about files can be moved from one system
// to another portably. Not all bits apply to all systems.
// The only required bit is ModeDir for directories.
type FileMode = fs.FileMode
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("无法打开文件:", err)
return
}
defer file.Close() // 确保在函数退出时关闭文件
bs := make([]byte, s.Size())
file.Read(bs)
// 打印文件内容
log.Println(string(bs))
root := "/usr/local/go/bin"
fileSystem := os.DirFS(root) // 返回 fs.FS
03
root := "/local/xxx/static"
rootfs := os.DirFS(root) // 返回 fs.FS
fsstatic := http.FileServer(http.FS(rootfs))
// 设置路由
http.Handle("/static/", http.StripPrefix("/static/", fsstatic))
04
//go:embed hello.txt
var s string
//go:embed hello.txt
var b []byte
//go:embed hello.txt
var f embed.FS
/*
Open 打开指定的文件进行读取,并将其作为fs.File返回。
当文件不是目录时, 返回的文件实现io.Seeker和io.ReaderAt 。
*/
func (f FS) Open(name string) (fs.File, error)
/*
ReadDir 读取并返回整个命名目录。
*/
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)
/*
ReadFile 读取并返回指定文件的内容。
*/
func (f FS) ReadFile(name string) ([]byte, error)
示例代码:从embed.FS读取文件目录,发布成http静态资源服务
package main
import (
"embed"
"log"
"net/http"
)
//go:embed static/*
var staticFiles embed.FS
func main() {
// 创建文件服务器
fileServer := http.FileServer(http.FS(staticFiles))
// 设置路由
http.Handle("/static/", http.StripPrefix("/static/", fileServer))
// 启动HTTP服务器
log.Println("Server started on: http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
05
END
参考资料:
[1]. https://pkg.go.dev/io@go1.22.1 官方IO类库
[2]. https://pkg.go.dev/embed@go1.22.1 官方Embed类库
[3]. https://github.com/jhunters/goassist goassist 开源go扩展库,提供了非常多的工具方法封装
推荐阅读: