包ioutil实现一些I/O实用程序函数
** ReadAll 方法**
外部调用方法, 封装了 readAll, 设置 缓冲片大小为512字节,传参由 2个减少至了一个,
从r 读取器 读取,直到出现错误或EOF并返回它读取的数据。 成功的调用返回errnil,而不是errEOF
因为ReadAll定义为从src读取直到EOF,所以它不会将EOF from read视为要报告的错误。

func ReadAll(r io.Reader) ([]byte, error) {
	return readAll(r, bytes.MinRead)		//  MinRead = 512
	// MinRead是Buffer.ReadFrom传递给读取调用的最小片大小。 只要缓冲区中至少有MinRead字节 > r字节,ReadFrom就不会增长底层缓冲区
}

** readAll 内部读取 方法**
从r读取数据直到EOF或遇到error,返回读取的数据和可能的错误。成功的调用返回的err为nil而非EOF。因为本函数定义为读取r直到EOF,它不会将读取返回的EOF视为应报告的错误。

func readAll(r io.Reader, capacity int64) (b []byte, err error) {
	var buf bytes.Buffer
	// 如果缓冲区溢出,我们将得到 bytes.ErrTooLarge.作为错误返回。其他的恐慌仍然存在
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
			err = panicErr
		} else {
			panic(e)
		}
	}()
	if int64(int(capacity)) == capacity {
		buf.Grow(int(capacity))
	}
	_, err = buf.ReadFrom(r)
	return buf.Bytes(), err
}

ReadAll 应用
// 1、open打开文件进行读取,返回*File类型,该类型实现了io.Reader 接口
// 2、 作为 io.Reader 类型传参, 读取该文件,返回出[]byte
// 该方法 内:将 *File类型的file文件操作句柄 转成了 实现了 Closer关闭接口的 具体 nopCloser 结构体实例,然后被当成 io.ReadCloser 接口来返回
// 返回的 nopCloser结构体 本身是io.Reader 实现,又定义了close方法继而实现 Closer关闭接口,所以组合成了 定义的反参: io.ReadCloser 接口

func main()  {
	file, err := os.Open("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
	checkErr(err)
	fileBytes1, err := ioutil.ReadAll(file)
	checkErr(err)
	fmt.Println(string(fileBytes1))
	}

** ReadFile 方法**
ReadFile 从filename指定的文件中读取数据并返回文件的内容。对err的判断和ReadAll一样。

func ReadFile(filename string) ([]byte, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	var n int64 = bytes.MinRead

	if fi, err := f.Stat(); err == nil {
		// 作为readAll的初始容量,在Size为零的情况下使用Size+加一点额外的容量,以避免在Read填满缓冲区后进行另一次分配
		// readAll调用将廉价地读入其分配的内部缓冲区。
		// 如果大小不正确,我们要么在末端浪费一些空间,要么根据需要重新分配,但在绝大多数常见的情况下,我们会得到正确的结果
		if size := fi.Size() + bytes.MinRead; size > n {
			n = size
		}
	}
	return readAll(f, n)
}

ReadFile应用

func main()  {
	file, err := os.Open("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
	checkErr(err)
	// ReadFile 函数直接可以传文件 参数
	fileBytes2, err := ioutil.ReadFile("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
	checkErr(err)
	fmt.Println(string(fileBytes2))
	}

WriteFile 方法
函数向filename指定的文件中写入数据。如果文件不存在将按给出的perm权限创建文件,否则在写入数据之前清空文件。

func WriteFile(filename string, data []byte, perm os.FileMode) error {
	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
	if err != nil {
		return err
	}
	_, err = f.Write(data)
	if err1 := f.Close(); err == nil {
		err = err1
	}
	return err
}

WriteFile 应用
WriteFile 以权限将 图片数据 写入新 文件

func main()  {
	pictureFile, err := ioutil.ReadFile("C:/GoWorks/src/go_task/hello-word/io/out/picture.jpg")
	checkErr(err)
	ioutil.WriteFile("C:/GoWorks/src/go_task/hello-word/io/out/picture1.jpg", pictureFile, 0777)
	}

ReadDir 方法
读取dirname目录内的所有文件信息,注意此序列有序

func ReadDir(dirname string) ([]os.FileInfo, error) {
	f, err := os.Open(dirname)
	if err != nil {
		return nil, err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return nil, err
	}
	sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
	return list, nil
}

ReadDir 应用
返回目录里文件,[]os.FileInfo类型

func main()  {
	dir, err := ioutil.ReadDir("C:/GoWorks/src/go_task/hello-word/io/out")
	checkErr(err)
	for i, v := range dir {
		fmt.Println(i, v)
		fmt.Println(i, v.Name())
	}
}

nopCloser 结构体
nopCloser也是一个读取器,只不过是个 其字段就直接包含io.Reader读取器接口的结构体
/且该结构体还 带有Close()方法,虽然没有做操作,但是实现了 Closer 接口

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() error { return nil }

返回一个ReadCloser,其中包含一个 no-op Close方法,该方法包装提供的读取器r。

func NopCloser(r io.Reader) io.ReadCloser {
	return nopCloser{r}
}

nopCloser 应用
该方法 内:将 *File类型的file文件操作句柄 转成了 实现了 Closer关闭接口的 具体 nopCloser 结构体实例,然后被当成 io.ReadCloser 接口来返回
返回的 nopCloser结构体 本身是io.Reader 实现,又定义了close方法继而实现 Closer关闭接口,所以组合成了 定义的反参: io.ReadCloser 接口

func main()  {
	file, err := os.Open("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
	checkErr(err)
	//  file作为 io.Reader 类型传参, 返回出一个 ReadCloser接口
	/
	readCloser := ioutil.NopCloser(file)

TemFile 方法
TempFile在dir目录中创建一个新的临时文件, 打开文件进行读写,并返回os.File结果
文件名是通过采用模式并在末尾添加一个随机字符串来生成的。 如果模式包含“
”,则随机字符串将替换最后一个“*”
同时调用TempFile的多个程序不会选择同一个文件。
调用者可以使用f.Name()来查找文件的路径名。当不再需要时,调用者有责任删除文件。

func TempFile(dir, pattern string) (f *os.File, err error) {
	// 1、如果dir是空字符串,TempFile将使用临时文件的默认目录(请参阅临时目录).
	if dir == "" {
		dir = os.TempDir()
	}
	// 2、根据 pattern 进行分割 ,分割出前缀和后缀
	prefix, suffix, err := prefixAndSuffix(pattern)
	if err != nil {
		return
	}

	nconflict := 0
	// 循环 10000次 创建出 一个文件
	for i := 0; i < 10000; i++ {
		// 3、根据目录 创建新路径
		name := filepath.Join(dir, prefix+nextRandom()+suffix)
		// 4、打开该 目录下并 创建文件,读写方式
		f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
		// 5、如果该文件存在,且循环了10次以上,则 给随机数 上锁,并重置随机源继续 创建操作
		if os.IsExist(err) {
			if nconflict++; nconflict > 10 {
				randmu.Lock()
				rand = reseed()
				randmu.Unlock()
			}
			continue
		}
		break
	}
	return
}

内部方法prefixAndSuffix
按最后一个通配符“”分割模式,如果适用,返回前缀作为“”之前的部分,后缀作为“*”之后的部分。

func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
	if strings.ContainsRune(pattern, os.PathSeparator) {
		err = errPatternHasSeparator
		return
	}
	if pos := strings.LastIndex(pattern, "*"); pos != -1 {
		prefix, suffix = pattern[:pos], pattern[pos+1:]
	} else {
		prefix = pattern
	}
	return
}

TempFile 应用
创建临时文件

func main()  {
	tempFile, err := ioutil.TempFile("C:/GoWorks/src/go_task/hello-word/io/in", "temp*.jpg")
	checkErr(err)
	fmt.Println("已创建临时文件:目录", tempFile.Name())
}

TempDir方法
TempDir在dir目录中创建一个新的临时目录
通过采用模式并在末尾应用随机字符串来生成目录名。如果模式包含“”,则随机字符串将替换最后一个“”。TempDir返回新目录的名称。
源码和 TemFile类似

func TempDir(dir, pattern string) (name string, err error) {
	// 1、如果dir是空字符串,TempDir将使用临时文件的默认目录(请参阅临时目录).
	if dir == "" {
		dir = os.TempDir()
	}

	// 2、根据 pattern 进行分割 ,分割出前缀和后缀
	prefix, suffix, err := prefixAndSuffix(pattern)
	if err != nil {
		return
	}

	nconflict := 0
	for i := 0; i < 10000; i++ {
		try := filepath.Join(dir, prefix+nextRandom()+suffix)
		err = os.Mkdir(try, 0700)
		if os.IsExist(err) {
			if nconflict++; nconflict > 10 {
				randmu.Lock()
				rand = reseed()
				randmu.Unlock()
			}
			continue
		}
		if os.IsNotExist(err) {
			if _, err := os.Stat(dir); os.IsNotExist(err) {
				return "", err
			}
		}
		if err == nil {
			name = try
		}
		break
	}
	return
}

TemDir 应用

func main()  {
	tempDir, err := ioutil.TempDir("C:/GoWorks/src/go_task/hello-word/io/out", "temp")
	checkErr(err)
	fmt.Println("已创建文件夹,目录:" + tempDir)
	
	// 写入数据到临时文件
	_, err = tempFile.Write(pictureFile)
	//关闭文件流,不关将无法删除
	tempFile.Close()
}