在TypeScript Azure Function中处理HTTP Form-Data的方案咨询
解决方案
方案一:使用busboy库(推荐)
busboy支持流式解析Form-Data,适配Azure Functions的HttpRequest对象(v3/v4均兼容),能同时处理文本字段和文件。
步骤:
- 安装依赖
npm install busboy @types/busboy
- 实现代码
import { AzureFunction, Context, HttpRequest } from "@azure/functions"; import Busboy from "busboy"; import { Readable } from "stream"; const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> { return new Promise((resolve, reject) => { const busboy = new Busboy({ headers: req.headers }); const parseResult = { fields: {} as Record<string, string>, files: {} as Record<string, { buffer: Buffer, filename: string }> }; // 处理文本字段 busboy.on("field", (fieldName, value) => { parseResult.fields[fieldName] = value; }); // 处理文件,收集buffer busboy.on("file", (fieldName, fileStream, filename) => { const chunks: Buffer[] = []; fileStream.on("data", (chunk) => chunks.push(chunk)); fileStream.on("end", () => { parseResult.files[fieldName] = { buffer: Buffer.concat(chunks), filename: filename || "unknown" }; }); }); // 解析完成后的逻辑 busboy.on("finish", () => { context.log("解析得到文本字段:", parseResult.fields); context.log("解析得到文件数量:", Object.keys(parseResult.files).length); context.res = { status: 200, body: { message: "Form-Data解析成功", fields: parseResult.fields, fileInfo: Object.entries(parseResult.files).map(([name, file]) => ({ fieldName: name, filename: file.filename, size: file.buffer.length })) } }; resolve(); }); // 处理解析错误 busboy.on("error", (err) => { context.log.error("Form-Data解析失败:", err); context.res = { status: 500, body: { message: "解析请求数据失败" } }; reject(err); }); // 根据Azure Functions版本处理请求体 if (req.body instanceof Buffer) { Readable.from(req.body).pipe(busboy); } else if (req.body instanceof ReadableStream) { (req.body as unknown as NodeJS.ReadableStream).pipe(busboy); } else { context.res = { status: 400, body: "不支持的请求体类型" }; resolve(); } }); }; export default httpTrigger;
方案二:修复parse-multipart库的用法
parse-multipart本身支持解析文本字段,只需手动区分文件和文本类型即可。
步骤:
- 确保已安装依赖
npm install parse-multipart
- 实现代码
import { AzureFunction, Context, HttpRequest } from "@azure/functions"; import * as multipart from "parse-multipart"; import { Readable } from "stream"; const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> { // 从请求头获取Form-Data的boundary const contentType = req.headers["content-type"] || ""; const boundary = multipart.getBoundary(contentType); if (!boundary) { context.res = { status: 400, body: "无效的Form-Data请求,缺少boundary" }; return; } // 将请求体转为Buffer(兼容v3/v4) let rawBody: Buffer; try { if (req.body instanceof Buffer) { rawBody = req.body; } else if (req.body instanceof ReadableStream) { rawBody = await new Promise((resolve, reject) => { const chunks: Buffer[] = []; const stream = req.body as unknown as NodeJS.ReadableStream; stream.on("data", (chunk) => chunks.push(chunk)); stream.on("end", () => resolve(Buffer.concat(chunks))); stream.on("error", reject); }); } else { context.res = { status: 400, body: "请求体类型不支持" }; return; } } catch (err) { context.log.error("读取请求体失败:", err); context.res = { status: 500, body: "读取请求数据失败" }; return; } // 解析所有parts const parts = multipart.Parse(rawBody, boundary); const fields: Record<string, string> = {}; const files: Record<string, { buffer: Buffer, filename: string }> = {}; parts.forEach(part => { if (part.filename) { files[part.name] = { buffer: part.data, filename: part.filename }; } else { fields[part.name] = part.data.toString("utf8"); } }); // 使用解析结果 context.log("文本字段:", fields); context.log("文件信息:", files); context.res = { status: 200, body: { fields, files: Object.entries(files).map(([name, file]) => ({ fieldName: name, filename: file.filename, size: file.buffer.length })) } }; }; export default httpTrigger;
注意事项
- Azure Functions v4的
HttpRequest.body是ReadableStream,v3版本则是Buffer,代码中已做兼容处理。 - 处理大文件时,推荐使用busboy的流式处理,避免将整个文件加载到内存中。
- 确保请求的
Content-Type头部包含multipart/form-data及正确的boundary参数。
内容的提问来源于stack exchange,提问作者beto
相关产品推荐
相关产品推荐

