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

Go生成ZIP文件HTTP返回乱码问题求助(含S3 PDF打包场景)

问题描述

我用Go写了一段代码,从AWS S3存储桶获取PDF文件并动态生成包含该PDF的ZIP文件。用os.Create把ZIP写入磁盘时一切正常,但通过HTTP响应返回时,Postman收到的是乱码数据,我希望能得到正常的包含PDF的ZIP文件。

代码示例

func admin_invoice_download_handler_wo_disk(w http.ResponseWriter, r *http.Request) {
    fileName := "sample.pdf"

    // Initialize AWS S3 Config
    cfg, err := config.LoadDefaultConfig(context.TODO(),
        config.WithRegion("ap-south-1"),
    )
    if err != nil {
        log.Println("AWS S3 config setup not successful. ", err)
    }

    // Create S3 service client
    client := s3.NewFromConfig(cfg)

    result, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
        Bucket: aws.String("bucket-name"),
        Key:    aws.String("bucket-dir/sample.pdf"),
    })
    if err != nil {
        log.Print(err)
    }

    defer result.Body.Close()

    // body in byte
    body, err := io.ReadAll(result.Body)
    if err != nil {
        log.Print(err)
    }

    // creating a zip
    buf := new(bytes.Buffer)
    writer := zip.NewWriter(buf)
    f, err := writer.Create(fileName)
    if err != nil {
        log.Fatal(err)
    }
    _, err = f.Write([]byte(body))
    if err != nil {
        log.Fatal(err)
    }
    resp1 := &common_tools.MessageResponse{
        Message: "Invoice downloaded successfully. ",
    }
    err = writer.Close()
    if err != nil {
        log.Fatal(err)
    }

    w.Header().Set("Content-Disposition", "attachment; filename=invoices.zip")
    w.Header().Set("Content-Type", "application/zip")
    
    io.Copy(w, buf)
    json.NewEncoder(w).Encode(resp1)
}

Postman乱码响应示例

PKsample.pdf�wT�[�/�l@rVh@@���9眡AB�d�AA2�s�I@�(�$�E2�L���y��w����Z�ǸV��Ԯ���v���U{ê)+��'���`gK;��(�����X�[88ۀ ��N(70k� �6(70/�,.B9Yc��f�/=�����;�7i���eQ��v����      ��  A�Qn��V(7��(���l��r���"yg'w��(�����`�o���+.�jXڡ�~�+����n)��p޿���+�[����SЁ���?t�`8�OI�����B��1�z
����F!���`A>�=A!(�7��~�oє�CtQ���%G���� �����
�
�{���G�`
<�/p����.���, ��ȸ�,�m��d1~ܕ�C�|P~8*�����Pv(��Qs��߉�ں;�~M�X���`�%�   ��[�
@q���'����v� ��('w�qvrG9�������i�6����;��w�pGɢ���Q �*����!���삛�+���8#�d��N����ex@M(����B��8\��ⳅ1�Jʼn�˸/���Ĥw��.�UݗA��ս�p���Ԁ���S��Xf��mUS����c��=H�Iq�U(5b]���ҳ�}��V宀_i�u�1!J�8'���c.SW�g*���r�Ŧb��C҇o��������_���U��0$��KQ���-*��ٶe<�}���v�(�6Z}����^����������2#����2f�ͶL?b��`*��
׮�},����e�r�L�4O�d��X�x��b�[1<�cuӨ�)�k��L�P���KU�{�dU� ���3�
�-�-��Օ�l�����G+��u�3d2���%y��>�LT�������B��y@��L�e6T�]��.�]`�|'���2��(����䈩1ҔXAf���x���Kp����S�������F�8�my�Τ�������Qx�*N�iK����p��jD�E�

问题原因及修复方案

核心问题

你的代码在HTTP响应中同时输出了二进制ZIP数据和JSON文本,导致两种数据混合在一起,Postman无法识别正确的ZIP格式,所以显示为乱码。具体是这两行代码:

io.Copy(w, buf) // 输出ZIP二进制数据
json.NewEncoder(w).Encode(resp1) // 紧接着输出JSON文本

HTTP响应是连续的数据流,混合两种不同格式的内容后,无法被解析为正常的ZIP文件。

修复方案

方案1:只返回ZIP文件(推荐)

如果仅需要让用户下载ZIP文件,直接去掉JSON响应代码,并优化内存使用:

func admin_invoice_download_handler_wo_disk(w http.ResponseWriter, r *http.Request) {
    fileName := "sample.pdf"

    // Initialize AWS S3 Config
    cfg, err := config.LoadDefaultConfig(context.TODO(),
        config.WithRegion("ap-south-1"),
    )
    if err != nil {
        log.Println("AWS S3 config setup failed: ", err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    // Create S3 service client
    client := s3.NewFromConfig(cfg)

    result, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
        Bucket: aws.String("bucket-name"),
        Key:    aws.String("bucket-dir/sample.pdf"),
    })
    if err != nil {
        log.Print("Failed to get S3 object: ", err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    defer result.Body.Close()

    // 设置响应头(必须在写入内容前设置)
    w.Header().Set("Content-Disposition", "attachment; filename=invoices.zip")
    w.Header().Set("Content-Type", "application/zip")

    // 直接将ZIP写入ResponseWriter,无需中间buffer
    writer := zip.NewWriter(w)
    defer writer.Close()

    f, err := writer.Create(fileName)
    if err != nil {
        log.Print("Failed to create ZIP entry: ", err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }

    // 直接复制S3数据流到ZIP,避免一次性读入内存(适合大文件)
    _, err = io.Copy(f, result.Body)
    if err != nil {
        log.Print("Failed to write to ZIP: ", err)
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
}

优化说明:

  • 移除io.ReadAll,直接通过io.Copy将S3数据流复制到ZIP,节省内存
  • 直接将zip.Writer绑定到http.ResponseWriter,无需中间bytes.Buffer
  • 增加错误对应的HTTP状态码返回,让客户端感知错误

方案2:返回JSON消息+下载链接(可选)

如果必须返回成功消息,可改为返回包含预签名S3下载链接的JSON,让客户端自行下载,这种场景需调整业务逻辑,此处仅作参考。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:24:52