前端解析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
相关产品推荐
相关产品推荐

