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

Go开发上传REST API如何同时返回JSON统计数据与CSV/XLS失败文件

实现方案说明

HTTP协议本身不支持单个响应设置两个不同的Content-Type,所以不存在直接同时返回JSON和二进制文件的标准方法,但Go支持以下两种行业常用的替代实现,完全可以满足你的需求:

方案1:multipart/mixed 多部分响应(符合HTTP标准)

这个方案通过multipart/mixed类型的响应体,将JSON统计信息和文件拆分为两个独立的响应段返回,单次请求即可获取全部数据。

Go实现示例

package main

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"mime/multipart"
	"net/http"
)

type UploadStat struct {
	SuccessCount int `json:"success_count"`
	FailCount    int `json:"fail_count"`
}

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	// 模拟业务处理得到的统计数据和失败数据
	stat := UploadStat{
		SuccessCount: 92,
		FailCount:    8,
	}
	failRecords := [][]string{
		{"行号", "错误原因"},
		{"3", "手机号格式错误"},
		{"7", "身份证号重复"},
		// 剩余失败数据按需补充
	}

	// 设置响应头为multipart/mixed,生成随机分隔符
	mw := multipart.NewWriter(w)
	defer mw.Close()
	w.Header().Set("Content-Type", fmt.Sprintf("multipart/mixed; boundary=%s", mw.Boundary()))

	// 第一部分:写入JSON统计数据
	jsonPart, err := mw.CreatePart(map[string][]string{
		"Content-Type": {"application/json"},
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err := json.NewEncoder(jsonPart).Encode(stat); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// 第二部分:写入CSV文件
	csvPart, err := mw.CreatePart(map[string][]string{
		"Content-Type": {"text/csv"},
		"Content-Disposition": {`attachment; filename="fail_records.csv"`},
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	cw := csv.NewWriter(csvPart)
	if err := cw.WriteAll(failRecords); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	cw.Flush()
}

func main() {
	http.HandleFunc("/upload", uploadHandler)
	http.ListenAndServe(":8080", nil)
}

适用场景

  • 失败数据量较大的场景,不会额外增加数据体积
  • 客户端支持解析multipart响应

方案2:Base64嵌入JSON响应

将生成的CSV/XLS文件内容编码为Base64字符串,和统计数据一起放到JSON结构体中返回,整个响应的Content-Type为application/json。

Go实现示例

package main

import (
	"encoding/base64"
	"encoding/csv"
	"encoding/json"
	"bytes"
	"net/http"
)

type UploadResponse struct {
	SuccessCount int    `json:"success_count"`
	FailCount    int    `json:"fail_count"`
	FailFileB64  string `json:"fail_file_base64"` // CSV/XLS文件的Base64编码
	FileType     string `json:"file_type"`        // 标识文件是csv还是xls
}

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	// 模拟业务数据
	successCount := 92
	failCount := 8
	failRecords := [][]string{
		{"行号", "错误原因"},
		{"3", "手机号格式错误"},
		{"7", "身份证号重复"},
	}

	// 生成CSV文件到内存缓冲区
	var buf bytes.Buffer
	cw := csv.NewWriter(&buf)
	_ = cw.WriteAll(failRecords)
	cw.Flush()

	// 编码为Base64
	b64Str := base64.StdEncoding.EncodeToString(buf.Bytes())

	// 构造响应返回
	resp := UploadResponse{
		SuccessCount: successCount,
		FailCount:    failCount,
		FailFileB64:  b64Str,
		FileType:     "csv",
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(resp)
}

func main() {
	http.HandleFunc("/upload", uploadHandler)
	http.ListenAndServe(":8080", nil)
}

适用场景

  • 失败数据量较小的场景,实现成本极低
  • 客户端不想处理复杂的multipart解析,直接拿到Base64即可生成文件下载

选型建议

如果上传失败的数据量通常小于10M,优先选择方案2,前后端实现都更简单;如果失败数据量很大,选择方案1避免Base64带来的额外33%体积开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 14:15:04