不幸的是,在標準庫中沒有針對內存中的io.WriteSeeker
實現的現成解決方案。
但是和往常一樣,你可以隨時實現你自己的。這並不難。
的io.WriteSeeker
是io.Writer
和io.Seeker
,所以基本上你只需要實現2種方法:
Write(p []byte) (n int, err error)
Seek(offset int64, whence int) (int64, error)
閱讀他們的資料,他們應該如何表現這些方法的一般合同。
這是一個簡單的實現,它使用內存中的字節片([]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
}
是的,就是這樣。
測試它:
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))
輸出(嘗試在Go Playground):
hello
hello world
hello work!
hello gopher
東西可以改善:
創建mywriter
值與初始爲空buf
切片,但其容量很可能會覆蓋結果PDF文檔的大小。例如。如果你估計結果PDF文件是大約1 MB,創建具有容量的緩存爲2 MB這樣的:
my := &mywriter{buf: make([]byte, 0, 2<<20)}
內mywriter.Write()
時需要能力有待提高(與現有的內容複製過來),也可能是有利可圖使用更大的增量,例如在一定程度上增加了現有產能的兩倍,這爲未來的追加保留了空間並使重新分配最小化。
多麼真棒的答案,謝謝。小評論:在SeekEnd案例中,您需要增加偏移量,而不是減去偏移量。查看stdlib的reader.go以供參考。由於我在幾個項目中使用了這個功能,因此我將它編入了一個lib文件,並在許可證和自述文件中給了您相當的評價。檢查出來:https://github.com/orcaman/writerseeker – orcaman
@orcaman你是對的,修正它在我的例子。 – icza