zoukankan      html  css  js  c++  java
  • 使用unpackit包解压gz包遇到的一个问题与解决方案

    问题描述

    上一篇博客介绍了使用go解决文件压缩问题:使用Golang解压缩文件遇到的问题及解决方法

    文中用到了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
    }
    View Code

    改造后的项目

    改造之后的项目放在了github上,欢迎star:https://github.com/Wanghongw/unpackit

  • 相关阅读:
    通达OA二次开发 工作流表单中关联查询另外一个工作流方法(源代码)
    SpringMVC+ajaxFileUpload上传图片 IE浏览器弹下载框问题解决方式
    Javascript正则中的exec和match
    Java中byte转int的方法
    推断php操作mysql(添删改查)是否成功
    关于人工智能的一些思考~
    Tomcat的虚拟主机的配置
    最近遇到的若干技术问题
    2015年工作中遇到的问题:11-20
    2015年工作中遇到的问题:11-20
  • 原文地址:https://www.cnblogs.com/paulwhw/p/14341913.html
Copyright © 2011-2022 走看看