如何在 Go 中用 gzipped 版本替换文件变量?
How to replace file variable with gzipped version in Go?
我有以下 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
}
如果 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
你的代码中有不少错误。
作为 "pre-first",始终检查返回的错误!
首先,os.Open()
以只读模式打开文件。为了能够替换磁盘上的文件内容,您必须改为以读写模式打开它:
file, err := os.OpenFile(fileName, os.O_RDWR, 0)
下一步,当你打开一个 io.Closer
(*os.File
是一个 io.Closer
) 的东西时,确保你用 Close()
方法关闭它,最好作为延迟语句来完成。
接下来,*os.File
是一个 io.Reader
, but that is not the same thing as a byte slice []byte
. An io.Reader
may be used to read bytes into a byte slice. Use io.Copy()
将内容从文件复制到 gzip 流(最终将在缓冲区中)。
在某些情况下(你没有关闭 gzip.Writer
),你必须调用 gzip.Writer.Flush()
以确保所有内容都被刷新到它的 writer(在这种情况下是缓冲区)。请注意 gzip.Writer.Close()
也会刷新,因此这似乎是一个不必要的步骤,但必须完成,例如如果 gzip.Writer
的 Close()
也被称为延迟语句,因为那样它在我们使用缓冲区的内容之前可能不会执行。因为在我们的示例中,我们在 io.Copy()
之后关闭了 gzip writer,这将处理必要的刷新。
接下来,要替换原文件的内容,必须找回文件的开头进行替换。为此,您可以使用 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)
}
}
注意:以上代码将替换您磁盘上的文件内容。如果你不想要这个而你只需要压缩数据,你可以这样做。请注意,我使用了一个 io.Reader
类型的新 input
变量,因为 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: 如果您不想 "work" 使用压缩数据,但只想发送它,例如作为网络响应,您甚至不需要 bytes.Buffer
, you can just "stream" the compressed data to the 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 代码:
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
}
如果 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
你的代码中有不少错误。
作为 "pre-first",始终检查返回的错误!
首先,os.Open()
以只读模式打开文件。为了能够替换磁盘上的文件内容,您必须改为以读写模式打开它:
file, err := os.OpenFile(fileName, os.O_RDWR, 0)
下一步,当你打开一个 io.Closer
(*os.File
是一个 io.Closer
) 的东西时,确保你用 Close()
方法关闭它,最好作为延迟语句来完成。
接下来,*os.File
是一个 io.Reader
, but that is not the same thing as a byte slice []byte
. An io.Reader
may be used to read bytes into a byte slice. Use io.Copy()
将内容从文件复制到 gzip 流(最终将在缓冲区中)。
在某些情况下(你没有关闭 gzip.Writer
),你必须调用 gzip.Writer.Flush()
以确保所有内容都被刷新到它的 writer(在这种情况下是缓冲区)。请注意 gzip.Writer.Close()
也会刷新,因此这似乎是一个不必要的步骤,但必须完成,例如如果 gzip.Writer
的 Close()
也被称为延迟语句,因为那样它在我们使用缓冲区的内容之前可能不会执行。因为在我们的示例中,我们在 io.Copy()
之后关闭了 gzip writer,这将处理必要的刷新。
接下来,要替换原文件的内容,必须找回文件的开头进行替换。为此,您可以使用 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)
}
}
注意:以上代码将替换您磁盘上的文件内容。如果你不想要这个而你只需要压缩数据,你可以这样做。请注意,我使用了一个 io.Reader
类型的新 input
变量,因为 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: 如果您不想 "work" 使用压缩数据,但只想发送它,例如作为网络响应,您甚至不需要 bytes.Buffer
, you can just "stream" the compressed data to the 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
}
}
将自动检测并设置正确的内容类型。