踩坑:form-data既有专门的库也有node自带的,它们处理的数据类型是不同的,如下:
Node 18 以后:
Web FormData → 只能吃 Blob
form-data 库 → 才能吃 Buffer
自带的form-data直接用就行(const formData = new FormData()),不需要额外引入。
使用form-data库的话需要先安装,然后引入,最后才能用,如下: npm i form-data const FormData = require("form-data") const formData = new FormData()
tips:推荐使用form-data库不要用node自带的
Koa 默认 不解析 multipart/form-data 请求体,需要
用安装@koa/multer。
- 文件数据挂载在
ctx.request.file! - 文本字段在
ctx.request.body!(需要安装koa-bodyparser插件才能接收)
前端
const handleFileSelect = async (event) => {
const file = event.target.files[0];
if (!file) return;
if (!file.type.match('image.*')) {
alert('请选择图片文件');
return;
}
try {
const result = await translateImageFileApi(file, sourceLanguage, targetLanguage);
console.log("result", result);
setTranslatedText(result.content[0].dst);
} catch (error) {
console.error('OCR error:', error);
alert('OCR识别失败');
} finally {
setIsTranslating(false);
}
};
async function translateImageFileApi(file, fromLang = 'auto', toLang = 'zh') {
if (!file) {
throw new Error('请提供图片文件');
}
// 1. 创建 FormData
const formData = new FormData();
formData.append('image', file); // 字段名必须是 'image'
formData.append('from', fromLang); // 文本参数
formData.append('to', toLang); // 文本参数
try {
const response = await axios.post(
`/ecTranslationApi/translateImageFile`, // 替换为你的 Koa 地址
formData,
{
headers: {
// 不要写 'Content-Type': 'multipart/form-data'
// 让 axios 自动设置正确的 boundary
}
}
);
console.log("前端收到的响应数据:",response);
return response.data;
} catch (error) {
console.error('图片翻译失败:', error);
throw error;
}
}
后端
//安装相应的包
npm i @koa/multer multer
npm i form-data
npm i koa-bodyparser
//引入对应的包
const bodyParser = require('koa-bodyparser'); // 解析 JSON 和 form-urlencode
const multer = require('@koa/multer');
const FormData = require('form-data');
// 配置 multer(内存存储,不写磁盘)
const upload = multer({
storage: multer.memoryStorage()
});
async function translateImage(file, fromLang = 'auto', toLang = 'zh') {
try {
const accessToken = await getAccessToken();
const formData = new FormData();
formData.append('image', file.buffer, {
filename: 'image.jpg', // 或 image.png / image.webp
contentType: 'image/jpeg' // 可选,但建议匹配
});
formData.append('from', fromLang);
formData.append('to', toLang);
formData.append('v', 3);
//既有param参数又有body参数,传递方式如下
const url = `${serverConfig.BaiduTranslateConfig.imageTranslateUrl}?access_token=${accessToken}`;
const response = await axios.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
});
console.log("response", response);
} catch (error) {
console.error('图片翻译请求失败:', error.response?.data || error.message);
return {
success: false,
error: '网络请求失败'
};
}
}
async function translateImageFile(ctx) {
try {
console.log('ctx.request.file:', ctx.request.file);
console.log('ctx.request.body:', ctx.request.body);
// 1. 获取上传的文件
const file = ctx.request.file; // 注意:是 ctx.req.file,不是 ctx.request.body
if (!file) {
ctx.status = 400;
ctx.body = { success: false, error: '缺少 image 文件' };
return;
}
// 2. 获取文本参数(from / to)
const from = ctx.request.body.from || 'auto';
const to = ctx.request.body.to || 'zh';
// 3. 验证文件类型(可选但推荐)
const allowedMimeTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (!allowedMimeTypes.includes(file.mimetype)) {
ctx.status = 400;
ctx.body = { success: false, error: '仅支持 JPG、PNG、WebP 格式' };
return;
}
// 4. 调用百度图片翻译
const result = await translateImage(file, from, to);
console.log("图片翻译result", result);
// 5. 返回结果
ctx.body = result;
} catch (error) {
console.error('服务器内部错误:', error);
ctx.status = 500;
ctx.body = { success: false, error: '服务器内部错误' };
}
}
重点
前端:要构造multipart/form-data 【使用web的form-data】
后端koa/multer:负责“解析” multipart/form-data
后端再发请求时:重新“构造” multipart/form-data【使用form-data库】
最终对照表(记住这个)
| 位置 | 用什么 |
|---|---|
| React / 浏览器 | 原生 FormData |
| Koa 接收文件 | multer |
| Koa → 第三方 API | form-data 库 |
| axios (Node) | form-data |