Golang程序因损坏的gzip文件崩溃怎么解决
Golang程序因损坏的gzip文件崩溃怎么解决 各位Gopher们好! 在我的程序中,我读取一个压缩的tar.gz归档文件:如果归档文件有效,程序运行正常;但尽管我做了所有可能的错误处理,当归档文件损坏时,gzip库仍会导致程序崩溃。
我哪里做错了?
以下是堆栈跟踪信息:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x68 pc=0x47fc62]
goroutine 1 [running]:
panic(0x5d02c0, 0xc4200120c0)
/usr/lib/go-1.7/src/runtime/panic.go:500 +0x1a1
compress/gzip.(*Reader).Close(0x0, 0x6d28a0, 0xc420012140)
/usr/lib/go-1.7/src/compress/gzip/gunzip.go:287 +0x22
myutils.GetFilesFromArchive(0xc420fc5d60, 0x45, 0x0, 0x6d28a0, 0xc420012140)
/home/gopher/.go/src/myutils/myutils.go:362 +0x480
myutils.ExtractArchive(0xc42006e990, 0x22, 0xc420fc5d60, 0x45, 0x603076, 0x4, 0x0, 0x0, 0x0)
/home/gopher/.go/src/myutils/myutils.go:180 +0x41d
main.installPackage(0x60e2de, 0x49, 0xc42006e990, 0x22, 0x0, 0x0, 0x0)
/home/gopher/.go/src/non_comm/non_comm.go:166 +0x70d
main.main()
/home/gopher/.go/src/non_comm/non_comm.go:108 +0x302
以下是堆栈跟踪中提到的调用:
func GetFilesFromArchive(tarFile string) (*[]string, error) {
// 打开文件
gz, err := os.Open(tarFile)
defer gz.Close()
if err != nil {
return nil, err
}
gzr, err := gzip.NewReader(gz)
defer gzr.Close()
if err != nil {
return nil, err // <==== 这是myutils.go:362
}
func ExtractArchive(destDir, tarFile, ext string) (*ExtractionResult, error) {
// ...
filesInArchive, err := GetFilesFromArchive(tarFile)
if err != nil {
return nil, fmt.Errorf("读取归档文件时出错: %s", err) // <==== 这是myutils.go:180
}
有什么想法吗?
更多关于Golang程序因损坏的gzip文件崩溃怎么解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢Norbert!这完全说得通,但不知怎么我竟然没注意到这一点。
更多关于Golang程序因损坏的gzip文件崩溃怎么解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
[!QUOTE] bisco:
gzr, err := gzip.NewReader(gz) defer gzr.Close() if err != nil { return nil, err // <==== 这是 myutils.go:362 }
gzr 是 nil 而你却关闭了它。defer 应该在检查 error 返回值之后执行。
在你展示的代码的其他部分也存在类似问题,但 panic 恰好指出了这一部分。
从堆栈跟踪可以看出,程序在 gzip.NewReader() 返回错误后仍然调用了 gzr.Close(),此时 gzr 为 nil,导致了空指针解引用。
问题在于当 gzip.NewReader() 返回错误时,gzr 变量是 nil,但 defer gzr.Close() 仍然会在函数返回时执行。在 Go 中,即使函数提前返回,defer 语句也会被执行。
以下是修复后的代码:
func GetFilesFromArchive(tarFile string) (*[]string, error) {
// 打开文件
gz, err := os.Open(tarFile)
if err != nil {
return nil, err
}
defer gz.Close()
gzr, err := gzip.NewReader(gz)
if err != nil {
return nil, err
}
defer gzr.Close()
// 继续处理 tar 文件
// ... 你的其他代码
}
关键修改:
- 将文件打开后的错误检查移到
defer之前 - 将
gzip.NewReader()的错误检查移到defer gzr.Close()之前 - 只有当
gzip.NewReader()成功创建 reader 时,defer gzr.Close()才会在有效的 reader 上执行
这样当 gzip.NewReader() 返回错误时(比如文件损坏),函数会直接返回错误,而不会执行 defer gzr.Close(),避免了在 nil 指针上调用方法。
如果需要在错误情况下仍然确保某些资源被清理,可以使用条件判断:
func GetFilesFromArchive(tarFile string) (*[]string, error) {
gz, err := os.Open(tarFile)
if err != nil {
return nil, err
}
defer gz.Close()
gzr, err := gzip.NewReader(gz)
if err != nil {
return nil, err
}
// 只有当 gzr 不为 nil 时才设置 defer
if gzr != nil {
defer gzr.Close()
}
// 继续处理 tar 文件
// ... 你的其他代码
}
但第一种方法更简洁,因为当 gzip.NewReader() 返回错误时,gzr 就是 nil,不需要额外的条件检查。

