如何用Go中的gzip压缩文件替换文件变量?

时间:2016-10-19 11:45:35

标签: file go compression gzip

我有以下Go代码:

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
}

如果file,我想用gzip压缩版本替换metaGzip = true的文件内容。

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 个答案:

答案 0 :(得分:2)

您的代码中存在很多错误。

作为“pre-first”,请务必检查返回的错误!

首先,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()也会刷新,因此这似乎是一个不必要的步骤,但必须完成,例如Close()的{​​{1}}也被称为延迟状态,因为它在我们使用缓冲区的内容之前可能不会执行。因为在我们的考试中,我们在gzip.Writer之后关闭了gzip编写器,这将处理必要的刷新。

接下来,要替换原始文件的内容,必须回到要替换的文件的开头。为此,您可以使用File.Seek()

接下来,您可以再次使用io.Copy()将缓冲区的内容(gzip压缩数据)复制到文件中。

最后,由于gzip压缩内容很可能比原始文件大小短,因此必须按照gzip压缩内容的大小截断文件(否则原始文件的未压缩内容可能会留在那里)。

这是完整的代码:

io.Copy()

注意:以上代码将替换磁盘上的文件内容。如果你不想要这个并且你只需要压缩数据,你可以这样做。请注意,我使用了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

注意#2:如果您不想“处理”压缩数据,但您只想发送它,例如作为网络响应,您甚至不需要bytes.Buffer,您只需将压缩数据“流式传输”到http.ResponseWriter

看起来像这样:

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

将自动检测并设置正确的内容类型。