有在你的代碼相當多的錯誤。
作爲「預先優先」,始終檢查返回的錯誤!
首先,os.Open()
以只讀模式打開文件。爲了能夠替換硬盤上的文件內容,必須在讀寫模式,而不是打開它:
file, err := os.OpenFile(fileName, os.O_RDWR, 0)
接下來,當你打開的東西是一個io.Closer
(*os.File
是io.Closer
),請確保您用Close()
方法關閉它,最好做爲延期陳述。
接下來,*os.File
是io.Reader
,但這與字節片[]byte
不是一回事。可以使用io.Reader
來將字節讀入字節片段。使用io.Copy()
將文件中的內容複製到gzip流(它將在緩衝區中結束)。
在某些情況下(如果您不關閉gzip.Writer
),您必須調用gzip.Writer.Flush()
以確保將所有內容刷新到其寫入程序(在此情況下爲緩衝區)。請注意,gzip.Writer.Close()
也會被刷新,所以這可能看起來像是一個不必要的步驟,但必須完成,例如,如果gzip.Writer
的Close()
也被稱爲延遲狀態量,因爲在我們使用緩衝區的內容之前它可能不會被執行。由於在我們的考試中,我們在io.Copy()
之後關閉gzip作者,這將會照顧必要的沖洗。
接下來,要替換原始文件的內容,您必須找回要替換的文件的開頭。爲此,您可以使用File.Seek()
。
接下來,您可能會再次使用io.Copy()
將緩衝區的內容(gzip壓縮的數據)複製到文件中。最後,由於gzip壓縮的內容很可能會短於原始文件的大小,因此您必須按照gzip壓縮內容的大小截斷文件(否則原始文件的未壓縮內容可能會留在那裏)。
下面是完整的代碼:
file, err := os.OpenFile(fileName, os.O_RDWR, 0)
if err != nil {
log.Fatalf("Error opening %q: %v", fileName, err)
}
defer file.Close()
// Check if gzip should be applied
if *metaGzip {
var b = &bytes.Buffer{}
w := gzip.NewWriter(b)
if _, err := io.Copy(w, file); err != nil {
panic(err)
}
if err := w.Close(); err != nil { // This also flushes
panic(err)
}
if _, err := file.Seek(0, 0); err != nil {
panic(err)
}
if _, err := io.Copy(file, b); err != nil {
panic(err)
}
if err := file.Truncate(int64(b.Len())); err != nil {
panic(err)
}
}
注:上面的代碼將取代磁盤上的文件內容。如果你不想要這個,你只需要壓縮的數據,你可以這樣做。請注意,我使用了一個新的input
變量io.Reader
,因爲bytes.Buffer
(或*bytes.Buffer
)的值不能被分配給類型爲*os.File
的變量,我們很可能只需要將結果作爲io.Reader
的值(並且已實施雙方):
var input io.Reader
file, err := os.Open(fileName)
if err != nil {
log.Fatalf("Error opening %q: %v", fileName, err)
}
defer file.Close()
// Check if gzip should be applied
if *metaGzip {
var b = &bytes.Buffer{}
w := gzip.NewWriter(b)
if _, err := io.Copy(w, file); err != nil {
panic(err)
}
if err := w.Close(); err != nil { // This also flushes
panic(err)
}
input = b
} else {
input = file
}
// Use input here
注2:如果你不想「工作」與壓縮數據,但你只是想送它如作爲網絡響應,您甚至不需要bytes.Buffer
,您可以將壓縮數據「流」到http.ResponseWriter
。
它看起來是這樣的:
func myHandler(w http.ResponseWriter, r *http.Request) {
file, err := os.Open(fileName)
if err != nil {
http.NotFound(w, r)
}
defer file.Close()
gz := gzip.NewWriter(w)
defer gz.Close()
if _, err := io.Copy(gz, file); err != nil {
// handle error
}
}
適當的內容類型將被自動檢測和設置。
謝謝,學習Go,所以我仍然犯了很多錯誤... –
再次感謝你,這真的很有幫助,特別是解釋!能夠得到它與您的幫助在這裏工作:https://github.com/bobvanluijt/gcloud-storage-transfer-tool/blob/master/gcloud-st.go –
@BobvanLuijt作出一些改進/簡化,你可能想重新閱讀它。 – icza