drd43058 2017-08-23 10:09
浏览 119
已采纳

在Go中使用没有文件的io.WriteSeeker

I am using a third party library to generate PDFs. In order to write the PDF at the end (after all of content has been added using the lib's API), the pdfWriter type has a Write function that expects an io.WriteSeeker.

This is OK if I want to work with files, but I need to work in-memory. Trouble is, I can't find any way to do this - the only native type I found that implements io.WriteSeeker is File.

This is the part that works by using File for the io.Writer in the Write function of the pdfWriter:

fWrite, err := os.Create(outputPath)
if err != nil {
    return err
}

defer fWrite.Close()

err = pdfWriter.Write(fWrite)

Is there way to do this without an actual File? Like getting a []byte or something?

  • 写回答

1条回答 默认 最新

  • dssqq64884 2017-08-23 10:56
    关注

    Unfortunately there is no ready solution for an in-memory io.WriteSeeker implementation in the standard lib.

    But as always, you can always implement your own. It's not that hard.

    An io.WriteSeeker is an io.Writer and an io.Seeker, so basically you only need to implement 2 methods:

    Write(p []byte) (n int, err error)
    Seek(offset int64, whence int) (int64, error)
    

    Read the general contract of these methods in their documentation how they should behave.

    Here's a simple implementation which uses an in-memory byte slice ([]byte). It's not optimized for speed, this is just a "demo" implementation.

    type mywriter struct {
        buf []byte
        pos int
    }
    
    func (m *mywriter) Write(p []byte) (n int, err error) {
        minCap := m.pos + len(p)
        if minCap > cap(m.buf) { // Make sure buf has enough capacity:
            buf2 := make([]byte, len(m.buf), minCap+len(p)) // add some extra
            copy(buf2, m.buf)
            m.buf = buf2
        }
        if minCap > len(m.buf) {
            m.buf = m.buf[:minCap]
        }
        copy(m.buf[m.pos:], p)
        m.pos += len(p)
        return len(p), nil
    }
    
    func (m *mywriter) Seek(offset int64, whence int) (int64, error) {
        newPos, offs := 0, int(offset)
        switch whence {
        case io.SeekStart:
            newPos = offs
        case io.SeekCurrent:
            newPos = m.pos + offs
        case io.SeekEnd:
            newPos = len(m.buf) + offs
        }
        if newPos < 0 {
            return 0, errors.New("negative result pos")
        }
        m.pos = newPos
        return int64(newPos), nil
    }
    

    Yes, and that's it.

    Testing it:

    my := &mywriter{}
    var ws io.WriteSeeker = my
    
    ws.Write([]byte("hello"))
    fmt.Println(string(my.buf))
    
    ws.Write([]byte(" world"))
    fmt.Println(string(my.buf))
    
    ws.Seek(-2, io.SeekEnd)
    ws.Write([]byte("k!"))
    fmt.Println(string(my.buf))
    
    ws.Seek(6, io.SeekStart)
    ws.Write([]byte("gopher"))
    fmt.Println(string(my.buf))
    

    Output (try it on the Go Playground):

    hello
    hello world
    hello work!
    hello gopher
    

    Things that can be improved:

    • Create a mywriter value with an initial empty buf slice, but with a capacity that will most likely cover the size of the result PDF document. E.g. if you estimate the result PDFs are around 1 MB, create a buffer with capacity for 2 MB like this:
      my := &mywriter{buf: make([]byte, 0, 2<<20)}

    • Inside mywriter.Write() when capacity needs to be increased (and existing content copied over), it may be profitable to use bigger increment, e.g. double the current capacity to a certain extent, which reserves space for future appends and minimizes the reallocations.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 删除和修改功能无法调用
  • ¥15 kafka topic 所有分副本数修改
  • ¥15 小程序中fit格式等运动数据文件怎样实现可视化?(包含心率信息))
  • ¥15 如何利用mmdetection3d中的get_flops.py文件计算fcos3d方法的flops?
  • ¥40 串口调试助手打开串口后,keil5的代码就停止了
  • ¥15 电脑最近经常蓝屏,求大家看看哪的问题
  • ¥60 高价有偿求java辅导。工程量较大,价格你定,联系确定辅导后将采纳你的答案。希望能给出完整详细代码,并能解释回答我关于代码的疑问疑问,代码要求如下,联系我会发文档
  • ¥50 C++五子棋AI程序编写
  • ¥30 求安卓设备利用一个typeC接口,同时实现向pc一边投屏一边上传数据的解决方案。
  • ¥15 SQL Server analysis services 服务安装失败