You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Azure下载Blob并解压的Go代码问题排查

问题描述

我有一段从Azure Storage下载Blob并解压的Go代码,但解压函数抛出错误:bytes.Reader.ReadAt: negative offsetBlob decompressed contents。传入readBlob函数的参数是正确的,代码能打印出Zip文件内的文件名,但无法打开Zip中的文件。直接通过Azure Storage Explorer下载该文件,用Windows资源管理器可正常打开Zip及其中内容。

代码如下:

func readBlob(client *azblob.Client, ctx context.Context, containerName string, blobName string) {
    // download the blob
    // will want to do this in the worker pool and not in the main loader thread
    get, err := client.DownloadStream(ctx, containerName, blobName, nil)
    handleError(err)

    downloadedData := bytes.Buffer{}
    retryReader := get.NewRetryReader(ctx, &azblob.RetryReaderOptions{})
    _, err = downloadedData.ReadFrom(retryReader)
    handleError(err)

    err = retryReader.Close()
    handleError(err)

    file, err := decompress(blobName, buff)
    if err != nil {
        fmt.Println("Error decompressing blob: ", blobName)
        fmt.Print(err)
    }
}

func decompress(blobName string, downloadedData bytes.Buffer) (string, error) {
    // try to decompress as zip
    zipReader, err := zip.NewReader(bytes.NewReader(downloadedData.Bytes()), int64(len(downloadedData.Bytes())))
    if err != nil {
        return "", err
    }

    for _, f := range zipReader.File {
    // the next line can add uncomment for troubleshooting and DOES print the right file name -
        does not necessary mean the blob download or the synnex of NewReader is
        perfect - but does validate it somewhat
    // fmt.Println("ffile name in zip file: ", f.Name)
        rc, err := f.Open()  // THIS IS where the error is returned
        if err != nil {
            return "", err
        }
        defer rc.Close()

        decompressedData, err := io.ReadAll(rc)
        if err != nil {
            return "", err
        }
        return string(decompressedData), nil // only care about the first file in the zip file
    }
}
问题分析与解决

1. 变量传递笔误

readBlob函数最后调用decompress时传入的是buff,但实际存储Blob数据的变量是downloadedData。这个错误会导致解压函数拿到空数据或错误数据,直接引发后续解压异常,修正为传入downloadedData即可。

2. ZipReader大小参数不准确

zip.NewReader的第二个参数需要准确传入Zip文件的总字节数。当前代码用int64(len(downloadedData.Bytes()))做类型转换,虽然逻辑上可行,但更稳妥的方式是直接使用downloadedData.Size()(bytes.Buffer的Size()方法直接返回int64类型的内容长度,避免转换误差)。如果大小参数与实际数据长度不匹配,Zip内部的偏移计算会出错,导致ReadAt出现负偏移错误。

3. Blob数据未完整下载

虽然能看到Zip内的文件名,但可能只下载了Zip的目录部分,文件内容未完整下载。可以添加下载完整性校验:

  • 先获取Blob属性拿到预期大小
  • 下载后对比实际读取的字节数与预期大小是否一致

修正后的代码示例

func readBlob(client *azblob.Client, ctx context.Context, containerName string, blobName string) {
    // 获取Blob属性,确认预期大小
    props, err := client.GetBlobProperties(ctx, containerName, blobName, nil)
    handleError(err)
    expectedSize := props.ContentLength

    // 下载Blob
    get, err := client.DownloadStream(ctx, containerName, blobName, nil)
    handleError(err)

    downloadedData := bytes.Buffer{}
    retryReader := get.NewRetryReader(ctx, &azblob.RetryReaderOptions{})
    readBytes, err := downloadedData.ReadFrom(retryReader)
    handleError(err)

    // 校验下载完整性
    if readBytes != expectedSize {
        fmt.Printf("下载不完整:预期%d字节,实际%d字节\n", expectedSize, readBytes)
        return
    }

    err = retryReader.Close()
    handleError(err)

    // 修正变量传递错误
    file, err := decompress(blobName, downloadedData)
    if err != nil {
        fmt.Println("Error decompressing blob: ", blobName)
        fmt.Print(err)
    }
}

func decompress(blobName string, downloadedData bytes.Buffer) (string, error) {
    // 使用准确的文件大小初始化ZipReader
    zipReader, err := zip.NewReader(bytes.NewReader(downloadedData.Bytes()), downloadedData.Size())
    if err != nil {
        return "", err
    }

    for _, f := range zipReader.File {
        rc, err := f.Open()
        if err != nil {
            return "", err
        }
        defer rc.Close()

        decompressedData, err := io.ReadAll(rc)
        if err != nil {
            return "", err
        }
        return string(decompressedData), nil
    }
    return "", fmt.Errorf("zip文件中无有效内容")
}

额外排查步骤

如果问题仍存在,可将下载的downloadedData写入本地文件,检查该文件是否能正常解压,以此确认下载的数据是否完整、未被篡改。

内容的提问来源于stack exchange,提问作者inf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 14:25:58