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

如何使用TypeScript向REST API正确发送ZIP文件数据

问题根因

后端接收参数为null核心是三个问题:

  • 前端发送请求时未和后端对齐Content-Type与参数序列化规则,后端无法按预期解析数据
  • 手动拆分base64的逻辑存在冗余,未做非空判断,容易传递无效值
  • 若后端默认按multipart表单格式接收文件,直接传纯base64字符串自然无法被正确识别

推荐实现方案(二进制直传,性能最优)

不要用dataURL格式读取base64内容,直接拿原始File对象走multipart/form-data格式上传,可省去30%左右的base64编解码开销,不会出现ZIP文件损坏问题,后端处理逻辑更简单。

前端TypeScript代码修正

  1. 直接存储原始文件对象,不需要等待组件读成base64
// 选中文件时直接存原始二进制对象
handleFileInputChange = (_event, file) => {
    this.setState({ 
      filename: file.name,
      rawFile: file // 新增字段存原始文件
    });
}

// 清空时同步清理文件缓存
handleClear = _event => this.setState({ filename: '', rawFile: null, fileData: '' });

handleFileReadStarted = _fileHandle => this.setState({ isLoading: true });
handleFileReadFinished = _fileHandle => this.setState({ isLoading: false });

// 上传逻辑
saveFileData = (filename: string, rawFile: File) => {
    if (!rawFile) {
      this.setState({fileSavestatusText: "请先选择ZIP文件"});
      return;
    }
    // 构造表单对象
    const formData = new FormData();
    formData.append("archive", rawFile, filename); // 字段名archive和后端保持一致即可

    // 注意:发请求时不要手动设置Content-Type,浏览器会自动带上multipart/form-data和正确的boundary参数
    API.sendZipFile(formData).then(response => {
      this.setState({
        fileSavestatusText: response.data,
      });
    }).catch(err => {
      this.setState({fileSavestatusText: `上传失败:${err.message}`});
    });
};
  1. 调整组件配置,去掉不必要的base64读取逻辑
render(){
  const { filename, rawFile, isLoading } = this.state;
  return (
    <>
    <FileUpload
      id="archive-file"
      type="file" // 改为file模式,直接拿原始文件,不读base64
      filename={filename}
      filenamePlaceholder="Drag and drop a file or upload one"
      onFileInputChange={this.handleFileInputChange}
      onReadStarted={this.handleFileReadStarted}
      onReadFinished={this.handleFileReadFinished}
      onClearClick={this.handleClear}
      hideDefaultPreview
      browseButtonText="Upload"
      height='425px'
      width='810px'
     >
      {filename && (
       <div className="pf-u-m-md">
        <FileUploadIcon size="md" /> Custom preview here for your file named {filename}
        </div>
        )}
    </FileUpload>
    <Button className={buttonstyle} variant="primary" onClick={() => 
      this.saveFileData(this.state.filename, this.state.rawFile)}>
      Save
    </Button>
    </>
 );
}

type设为file后,不需要再监听onDataChange/onTextChange事件读取文件内容,可减少不必要的内存占用。


备选方案(base64格式传输)

如果业务场景必须传递base64字符串,按如下逻辑修正:

saveFileData = (filename: string, fileData: string) => {
    if (!fileData || !fileData.startsWith("data:application/")) {
      this.setState({fileSavestatusText: "无效的文件数据"});
      return;
    }
    // 拆分data头和base64内容,去掉无意义的循环
    const [meta, base64Data] = fileData.split(",");
    const fileType = meta.match(/data:(.*?);base64/)?.[1] || "application/zip";

    // 构造明确的JSON请求体,字段和后端对齐
    const payload = {
      filename: filename,
      file_type: fileType,
      file_base64: base64Data
    };

    // 该请求必须设置Content-Type: application/json头
    API.sendFileNameAndFileData(payload).then(response => {
      this.setState({
        fileSavestatusText: response.data,
      });
    });
};

Golang后端接收示例

对应multipart上传方式(推荐)

直接解析表单文件存储到/temp目录即可:

func UploadZipHandler(w http.ResponseWriter, r *http.Request) {
    // 限制上传大小为1G,可按需调整
    r.ParseMultipartForm(1 << 30)
    file, header, err := r.FormFile("archive") // 和前端formData字段名保持一致
    if err != nil {
        http.Error(w, "接收文件失败", http.StatusBadRequest)
        return
    }
    defer file.Close()

    // 处理文件名,防止路径穿越
    savePath := filepath.Join("/tmp", filepath.Base(header.Filename))
    dst, err := os.Create(savePath)
    if err != nil {
        http.Error(w, "创建文件失败", http.StatusInternalServerError)
        return
    }
    defer dst.Close()

    // 写入文件
    _, err = io.Copy(dst, file)
    if err != nil {
        http.Error(w, "存储文件失败", http.StatusInternalServerError)
        return
    }

    w.Write([]byte(fmt.Sprintf("文件上传成功,存储路径:%s", savePath)))
}

对应base64上传方式

解析JSON后解码base64存储:

type ZipUploadReq struct {
    Filename   string `json:"filename"`
    FileType   string `json:"file_type"`
    FileBase64 string `json:"file_base64"`
}

func UploadZipBase64Handler(w http.ResponseWriter, r *http.Request) {
    var req ZipUploadReq
    err := json.NewDecoder(r.Body).Decode(&req)
    if err != nil {
        http.Error(w, "解析请求失败", http.StatusBadRequest)
        return
    }

    // 解码base64
    fileData, err := base64.StdEncoding.DecodeString(req.FileBase64)
    if err != nil {
        http.Error(w, "base64解码失败", http.StatusBadRequest)
        return
    }

    savePath := filepath.Join("/tmp", filepath.Base(req.Filename))
    err = os.WriteFile(savePath, fileData, 0644)
    if err != nil {
        http.Error(w, "存储文件失败", http.StatusInternalServerError)
        return
    }

    w.Write([]byte(fmt.Sprintf("文件上传成功,存储路径:%s", savePath)))
}

避坑提醒
  • 用multipart上传时绝对不要手动设置Content-Type头,手动设置会丢失boundary参数导致后端解析失败
  • 存储文件时必须用filepath.Base()处理文件名,防止恶意用户传带../的文件名穿越目录写入其他位置
  • 如果用Nginx等反向代理,记得调整client_max_body_size参数,避免大ZIP包被拦截
  • 上传完成后可校验ZIP文件魔数(前2字节为PK即0x50、0x4B),过滤损坏或格式错误的文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 23:48:50