React Native + Expo应用通过Azure Functions(Node.js)上传图片至Azure Blob Storage后无法正常打开的问题及正确上传方法问询
React Native Expo + Azure Functions: Fix Corrupted JPEG Uploads to Azure Blob Storage
我之前也碰到过一模一样的问题,你的排查思路完全正确——服务端解析后的二进制数据开头多出来的0d 0a(回车换行)破坏了JPEG的文件头(JPEG必须以FF D8开头),导致图片无法正常打开。下面给你两个可行的解决方案,以及问题的根源分析:
解决方案1:修正Multipart/Form-Data的解析流程
这个方案适配你当前的FormData上传方式,主要从客户端和服务端两方面调整:
客户端(React Native + Expo)修改
问题出在手动设置空headers,导致fetch无法自动生成带正确boundary的Content-Type头,服务端解析时出现异常。修改代码如下:
const formData = new FormData(); const imageUri = params.uri; const dataType = mime.getType(imageUri); const fileName = imageUri.split("/").pop(); // 移除不必要的类型断言,确保字段格式正确 formData.append('image', { uri: imageUri, name: fileName || "a.jpg", type: dataType || "image/jpeg" }); const url = 'xxxxx'; // 不要手动设置headers,让fetch自动处理multipart的Content-Type和boundary await fetch(url, { method: 'POST', body: formData });
服务端(Azure Functions Node.js)修改
替换parse-multipart库为更可靠的busboy,它能准确解析multipart/form-data,避免混入额外字符:
- 先安装busboy:
npm install busboy - 修改函数代码:
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> { try { const { BlobServiceClient } = require("@azure/storage-blob"); const busboy = require("busboy"); const containerName = "ContainerName"; const connectionString = "your-storage-connection-string"; return new Promise((resolve, reject) => { const bb = busboy({ headers: req.headers }); let blobName: string; let blobData: Buffer[] = []; // 监听文件上传事件 bb.on("file", (name: string, file: any, info: { filename: string }) => { blobName = info.filename; file.on("data", (data: Buffer) => { blobData.push(data); }); file.on("end", async () => { // 拼接二进制数据 const buffer = Buffer.concat(blobData); // 上传到Blob Storage const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString); const containerClient = blobServiceClient.getContainerClient(containerName); const blockBlobClient = containerClient.getBlockBlobClient(blobName); const uploadResponse = await blockBlobClient.upload(buffer, buffer.length); context.log(`Blob uploaded successfully, requestId: ${uploadResponse.requestId}`); context.res = { status: 200, body: "Upload successful" }; resolve(); }); }); // 处理解析错误 bb.on("error", (err: Error) => { context.log(`Error parsing form data: ${err.message}`); context.res = { status: 500, body: "Error processing upload" }; reject(err); }); req.pipe(bb); }); } catch (error) { context.log(`Error: ${error.message}`); context.res = { status: 500, body: "Internal server error" }; } };
解决方案2:直接上传Base64数据(更简单)
既然你已经在ImagePicker中开启了base64: true,可以跳过multipart解析,直接上传base64字符串,避免解析问题:
客户端修改
const url = 'xxxxx'; const fileName = params.uri.split("/").pop(); await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ imageBase64: params.base64, fileName: fileName || "a.jpg" }) });
服务端修改
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> { try { const { BlobServiceClient } = require("@azure/storage-blob"); const containerName = "ContainerName"; const connectionString = "your-storage-connection-string"; const { imageBase64, fileName } = req.body; // 将base64转成Buffer const buffer = Buffer.from(imageBase64, 'base64'); // 上传到Blob Storage const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString); const containerClient = blobServiceClient.getContainerClient(containerName); const blockBlobClient = containerClient.getBlockBlobClient(fileName); const uploadResponse = await blockBlobClient.upload(buffer, buffer.length); context.log(`Blob uploaded successfully, requestId: ${uploadResponse.requestId}`); context.res = { status: 200, body: "Upload successful" }; } catch (error) { context.log(`Error: ${error.message}`); context.res = { status: 500, body: "Internal server error" }; } };
问题根源说明
- 客户端:手动设置空headers会导致fetch无法自动添加
Content-Type: multipart/form-data; boundary=xxx,服务端解析multipart数据时无法正确识别分隔符,从而引入额外的换行符。 - 服务端:
parse-multipart库在处理二进制数据时,对boundary的换行处理不够严谨,导致0d 0a被混入JPEG文件头,破坏了文件结构。而Postman上传正常是因为它会自动生成标准的multipart请求头,服务端解析时没有异常。
这两个方案都能解决你的问题,方案2更简单,适合小尺寸图片;方案1更适合大文件上传(避免base64的体积膨胀)。
内容的提问来源于stack exchange,提问作者Yutaro
相关产品推荐
相关产品推荐

