如何连接io.Reader和io.Writer?

20

我正在编写一个长时间运行的任务,需要多次从MongoDB(使用mgo)中读取数据,并使用这个模块将其写入xlsx文件。然后再使用os.Open读取文件并将其存储到FTP服务器上。

Stor函数会消耗大量内存,因此我认为应该有一种方法可以直接从xlsx.Write将数据传递给ftp.Store,而不必保存文件。(如果我能同时进行流式传输就更完美了,因为在将它们发送到Stor函数之前,我不必将所有文档都保存在服务器内存中)

以下是这些函数的原型

func (f *File) Write(writer io.Writer) (err error) xlsl

func (ftp *FTP) Stor(path string, r io.Reader) (err error) ftp

2个回答

29

您想要使用io.Pipe。可以这样做:

reader, writer := io.Pipe()
errChan := make(chan error)
go func() {
    errChan <- myFTP.Stor(path, reader)
}()
err := myXLS.Write(writer)
// handle err
err = <-errChan
// handle err

如果 xlsx.Write 返回错误而未关闭 writer,则可能需要使用 writer.CloseWithError(err)


1
io.Pipe() 不会返回 io.Writer 和 io.Reader,而是会返回 PipeReader、PipeWriter。它们是一样的吗? - Methuz Kaewsai-kao
6
io.Reader 是一个接口,*PipeReader 是一个实现 io.Reader 接口的具体类型(写入器也是同样的情况)。因此,您可以在任何需要 io.Reader 的地方使用 *PipeReader - LeGEC
请注意,io.Pipe()创建了一个同步的内存管道。这意味着根据你从读取端读取的数据量,你可以写入的数据量是有限制的。例如,如果使用bytes.Buffer.ReadFrom()与读取端一起使用,这将限制要写入的数据量。(在我的情况下是64 KB,但可能根据架构和Go版本而有所不同) - undefined

2
你可以使用bytes.Buffer
func uploadFileToQiniu(file *xlsx.File) (key string, err error) {
    key = fmt.Sprintf("%s.xlsx", util.SerialNumber())
    log.Debugf("file key is %s", key)

    log.Debug("start to write file to a writer")
    buf := new(bytes.Buffer)
    err = file.Write(buf)
    if err != nil {
        log.Errorf("error caught when writing file: %v", err)
        return
    }

    size := int64(buf.Len())
    log.Debugf("file size is %d", size)
    err = Put(key, size, buf)
    if err != nil {
        log.Errorf("error caught when uploading file: %v", err)
    }
    return key, nil
}

func Put(key string, size int64, reader io.Reader) error {}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接