如何在Golang中从gzip或普通文本读取器中读取?

6

我正在使用golang编写一个小型的web应用程序,它涉及解析用户上传的文件。我希望自动检测文件是否已经gzip压缩,并相应地创建读取器/扫描器。其中一个难点是我不能将整个文件读入内存,只能在流上操作。以下是我的代码:

func scannerFromFile(reader io.Reader) (*bufio.Scanner, error) {

var scanner *bufio.Scanner
//create a bufio.Reader so we can 'peek' at the first few bytes
bReader := bufio.NewReader(reader)

testBytes, err := bReader.Peek(64) //read a few bytes without consuming
if err != nil {
    return nil, err
}
//Detect if the content is gzipped
contentType := http.DetectContentType(testBytes)

//If we detect gzip, then make a gzip reader, then wrap it in a scanner
if strings.Contains(contentType, "x-gzip") {
    gzipReader, err := gzip.NewReader(bReader)
    if (err != nil) {
        return nil, err
    }

    scanner = bufio.NewScanner(gzipReader)

} else {
    //Not gzipped, just make a scanner based on the reader
    scanner = bufio.NewScanner(bReader)
}

return scanner, nil
}

这对于普通文本来说很好用,但对于压缩数据来说,它解压出现问题,几千字节后不可避免地得到乱码。有没有更简单的方法?为什么在几千行后解压不正确?

让我想知道这段代码之外是否有问题——从gzip reader中得到乱码文本绝对不是我所期望的事情。(编辑:哎呀,那个“不”很重要。 :) ) - twotwotwo
1
代码看起来对我来说是正确的。我建议使用contentType == "application/x-gzip"而不是strings.Contains。 - Charlie Tumahai
1
如果压缩流本身损坏,我会期望你会得到一个CRC错误;可能是在压缩之前或解压之后的某个地方出了问题——无论如何,我担心这里可能没有足够的信息来解决问题。 - twotwotwo
2个回答

8
您可以通过检查前两个字节是否等于0x1f8b来检测文件是否被gzip压缩(我在这里找到了相关信息)。
在评论中,有人提到应该分别检查这两个字节,第一个是0x1f,第二个是0x8b
testBytes, err := bReader.Peek(2) //read 2 bytes
....
if testBytes[0] == 31 && testBytes[1] == 139 {
    //gzip
}else{
   ...
}

希望这能帮到您。

0

感谢大家 - 结果证明twotwotwo和thundercat是正确的,流在一个与我发布的代码无关的位置被破坏了。奇怪的是,似乎与在仍在读取请求流时写入http响应有关。我仍在调查中,但原始问题似乎是误导性的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接