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
相关产品推荐
相关产品推荐

