问题描述
上一篇博客介绍了使用go解决文件压缩问题:
文中用到了unpackit这个第三方包。
但是,实际中我在解压文件的时候遇到了这样的情况:在电脑手动解压的文件大概有120多k,文件是完整的,但是使用代码解压之后的文件竟然只有4k!
毫无疑问:文件中的数据也比完整的数据少了很多!
源码浅析
研究了一下unpackit这个包的源码,终于找到了问题所在!
我们可以在源码中的unpackit.go文件中找到下面这段代码:
switch ftype {
case "gzip":
gzr, err := gzip.NewReader(r)
if err != nil {
return "", err
}
defer func() {
if err := gzr.Close(); err != nil {
fmt.Printf("%+v", errors.Wrapf(err, "unpackit: failed closing gzip reader"))
}
}()
decompressingReader = bufio.NewReader(gzr)
其实问题就是出在了bufio.NewReader方法上了!
打开bufio.NewReader的源码,可以找到,官方默认只给缓冲区设置了4k的大小!
const (
defaultBufSize = 4096
)
// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
所以当我们解压之后的json文件超过了4k的话,程序会默认将后面的数据省略掉!
问题解决
既然找到了问题的原因所在,可以改造一下官方的源码,当我们获取到的文件不是一个xxx.tar.gz文件而是一个直接由源文件打包成的gz文件的话必须得设置一下缓冲区的大小!
改造后的Unpack函数如下:
func Unpack(reader io.Reader, destPath string, unTarName string, unTarGzSize int) (string, error) {
var err error
if destPath == "" {
destPath, err = ioutil.TempDir(os.TempDir(), "unpackit-")
if err != nil {
return "", err
}
}
// Makes sure destPath exists
if err := os.MkdirAll(destPath, os.ModePerm); err != nil {
return "", err
}
r := bufio.NewReader(reader)
// Reads magic number from the stream so we can better determine how to proceed
ftype, err := magicNumber(r, 0)
if err != nil {
return "", err
}
var decompressingReader *bufio.Reader
switch ftype {
case "gzip":
gzr, err := gzip.NewReader(r)
if err != nil {
return "", err
}
defer func() {
if err := gzr.Close(); err != nil {
fmt.Printf("%+v", errors.Wrapf(err, "unpackit: failed closing gzip reader"))
}
}()
// TODO:注意这里相当于:bufio.NewReaderSize(gzr, 4096),如果不是tar包并且文件大小超出了4k,会丢失数据
// TODO:需要加大一下缓冲区的大小!
// decompressingReader = bufio.NewReader(gzr)
decompressingReader = bufio.NewReaderSize(gzr, unTarGzSize)
case "xz":
xzr, err := xz.NewReader(r)
if err != nil {
return "", err
}
decompressingReader = bufio.NewReader(xzr)
case "bzip":
br, err := bzip2.NewReader(r, nil)
if err != nil {
return "", err
}
defer func() {
if err := br.Close(); err != nil {
fmt.Printf("%+v", errors.Wrapf(err, "unpackit: failed closing bzip2 reader"))
}
}()
decompressingReader = bufio.NewReader(br)
case "zip":
// Like TAR, ZIP is also an archiving format, therefore we can just return
// after it finishes
return Unzip(r, destPath)
default:
// maybe it is a tarball file
fmt.Println("进入了default!!!!! ")
decompressingReader = r
}
// Check magic number in offset 257 too see if this is also a TAR file
ftype, err = magicNumber(decompressingReader, 257)
if err != nil {
return "", err
}
if ftype == "tar" {
return Untar(decompressingReader, destPath)
}
// If it's not a TAR archive then save it to disk as is.
destRawFile := filepath.Join(destPath, sanitize(path.Base(unTarName)))
// Creates destination file
destFile, err := os.Create(destRawFile)
if err != nil {
return "", err
}
defer func() {
if err := destFile.Close(); err != nil {
log.Println(err)
}
}()
// Copies data to destination file
if _, err := io.Copy(destFile, decompressingReader); err != nil {
return "", err
}
return destPath, nil
}
改造后的项目
改造之后的项目放在了github上,欢迎star: