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

前端解析multipart/form-data返回的PNG与JSON时生成Blob报错求助

核心问题
  • 请求未指定正确的响应类型:默认情况下你的请求库会把响应解析为UTF-8字符串,PNG是二进制格式,转字符串的过程中字节会被强制编码,原始数据已经损坏,后续无论怎么转换都无法还原合法PNG。
  • 字符串分割逻辑不适用于二进制数据:你直接对已损坏的字符串按boundary分割,分割结果还包含了part头部的换行、空行等冗余数据,直接用来生成Blob必然不合法。
  • 自定义转换函数逻辑错误:stringToUint中先编码再转base64的流程会进一步篡改原始二进制字节,生成的Uint8Array和原始PNG数据完全不匹配。
正确实现方案

最简方案(推荐)

只要后端返回的每个part都设置了正确的Content-Disposition字段的name属性,直接用浏览器原生的FormData解析能力即可,无需手动分割:

// 请求时指定响应类型为blob
API.post(`api/endpoint`, data, { responseType: 'blob' }).then(async (resp) => {
  // 浏览器自动解析multipart内容
  const formData = await new Response(resp.data).formData();
  // 按后端设置的字段名直接提取内容,比如图片字段名是image,JSON字段名是info
  const pngBlob = formData.get('image');
  const jsonStr = formData.get('info');
  const jsonResponse = JSON.parse(jsonStr);
  // 生成图片可用的链接
  const pngUrl = URL.createObjectURL(pngBlob);
})

手动分割兼容方案

如果后端未设置标准的part字段名,可以手动解析二进制流:

API.post(`api/endpoint`, data, { responseType: 'arraybuffer' }).then((resp) => {
  // 1. 提取boundary
  const contentType = resp.headers['content-type'];
  const boundaryMatch = contentType.match(/boundary="?([^";]+)"?/);
  if (!boundaryMatch) return;
  const boundary = '--' + boundaryMatch[1];
  const boundaryBytes = new TextEncoder().encode(boundary);
  const uint8 = new Uint8Array(resp.data);

  // 2. 查找子数组位置的通用方法
  const findSubArrayIndex = (source, target) => {
    for (let i = 0; i <= source.length - target.length; i++) {
      let match = true;
      for (let j = 0; j < target.length; j++) {
        if (source[i + j] !== target[j]) {
          match = false;
          break;
        }
      }
      if (match) return i;
    }
    return -1;
  }

  // 3. 定位两个boundary的位置
  const firstBoundaryPos = findSubArrayIndex(uint8, boundaryBytes);
  const secondBoundaryPos = findSubArrayIndex(uint8.slice(firstBoundaryPos + boundaryBytes.length), boundaryBytes) + firstBoundaryPos + boundaryBytes.length;

  // 4. 提取PNG内容:跳过part头部,找到内容起始位置(连续两个\r\n之后是真实内容)
  const pngPart = uint8.slice(firstBoundaryPos + boundaryBytes.length, secondBoundaryPos);
  const rnrnBytes = new TextEncoder().encode('\r\n\r\n');
  const pngContentStart = findSubArrayIndex(pngPart, rnrnBytes) + 4;
  const pngBlob = new Blob([pngPart.slice(pngContentStart)], { type: 'image/png' });
  const pngUrl = URL.createObjectURL(pngBlob);

  // 5. 提取JSON内容
  const jsonPart = uint8.slice(secondBoundaryPos + boundaryBytes.length, uint8.length - boundaryBytes.length - 2);
  const jsonContentStart = findSubArrayIndex(jsonPart, rnrnBytes) + 4;
  const jsonStr = new TextDecoder().decode(jsonPart.slice(jsonContentStart));
  const jsonResponse = JSON.parse(jsonStr);
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 15:06:03