2016-10-19 62 views
1

我有以下Go代碼:如何在Go中用gzip版本替換文件變量?

file, err := os.Open(fileName) 
if err != nil { 
    fatalf(service, "Error opening %q: %v", fileName, err) 
} 

// Check if gzip should be applied 
if *metaGzip == true { 
    var b bytes.Buffer 
    w := gzip.NewWriter(&b) 
    w.Write(file) 
    w.Close() 
    file = w 
} 

我想用gzip壓縮的版本,如果metaGzip = true更換的file文件內容。

PS:
我也跟着這樣的建議:Getting "bytes.Buffer does not implement io.Writer" error message但我仍然得到錯誤:cannot use file (type *os.File) as type []byte in argument to w.Write

回答

1

有在你的代碼相當多的錯誤。

作爲「預先優先」,始終檢查返回的錯誤!

首先,os.Open()以只讀模式打開文件。爲了能夠替換硬盤上的文件內容,必須在讀寫模式,而不是打開它:

file, err := os.OpenFile(fileName, os.O_RDWR, 0) 

接下來,當你打開的東西是一個io.Closer*os.Fileio.Closer),請確保您用Close()方法關閉它,最好做爲延期陳述。

接下來,*os.Fileio.Reader,但這與字節片[]byte不是一回事。可以使用io.Reader來將字節讀入字節片段。使用io.Copy()將文件中的內容複製到gzip流(它將在緩衝區中結束)。

在某些情況下(如果您不關閉gzip.Writer),您必須調用gzip.Writer.Flush()以確保將所有內容刷新到其寫入程序(在此情況下爲緩衝區)。請注意,gzip.Writer.Close()也會被刷新,所以這可能看起來像是一個不必要的步驟,但必須完成,例如,如果gzip.WriterClose()也被稱爲延遲狀態量,因爲在我們使用緩衝區的內容之前它可能不會被執行。由於在我們的考試中,我們在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 
    } 
} 

適當的內容類型將被自動檢測和設置。

+0

謝謝,學習Go,所以我仍然犯了很多錯誤... –

+0

再次感謝你,這真的很有幫助,特別是解釋!能夠得到它與您的幫助在這裏工作:https://github.com/bobvanluijt/gcloud-storage-transfer-tool/blob/master/gcloud-st.go –

+1

@BobvanLuijt作出一些改進/簡化,你可能想重新閱讀它。 – icza

相關問題