我正在使用第三方库来生成PDF.为了在最后编写PDF(在使用lib的API添加所有内容之后),该pdfWriter类型具有Write期望的函数io.WriteSeeker.
如果我想处理文件,这是可以的,但我需要在内存中工作.麻烦的是,我找不到任何办法 - 我发现实现的唯一本机类型io.WriteSeeker是File.
这是通过使用作品的部分File为io.Writer在Write的功能pdfWriter:
fWrite, err := os.Create(outputPath)
if err != nil {
return err
}
defer fWrite.Close()
err = pdfWriter.Write(fWrite)
Run Code Online (Sandbox Code Playgroud)
没有实际的文件,有没有办法做到这一点?喜欢得到[]byte什么?
遗憾的是io.WriteSeeker,在标准库中没有现成的内存实现解决方案.
但与往常一样,您可以随时实施自己的.这并不难.
An io.WriteSeeker是an io.Writer和an io.Seeker,所以基本上你只需要实现2个方法:
Write(p []byte) (n int, err error)
Seek(offset int64, whence int) (int64, error)
Run Code Online (Sandbox Code Playgroud)
在他们的文档中阅读这些方法的一般合同应该如何表现.
这是一个使用内存中字节slice([]byte)的简单实现.它没有针对速度进行优化,这只是一个"演示"实现.
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
}
Run Code Online (Sandbox Code Playgroud)
是的,就是这样.
测试它:
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))
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
hello
hello world
hello work!
hello gopher
Run Code Online (Sandbox Code Playgroud)
可以改进的事情:
mywriter使用初始空buf切片创建一个值,但其容量很可能会覆盖结果PDF文档的大小.例如,如果您估计结果PDF大约为1 MB,请创建一个容量为2 MB的缓冲区,如下所示:
my := &mywriter{buf: make([]byte, 0, 2<<20)}
在mywriter.Write()需要增加容量(并且复制现有内容)时,使用更大的增量(例如,在一定程度上将当前容量加倍)可能是有利可图的,这为将来的附加预留空间并最小化重新分配.