Nuxt3上传音频至Whisper API遇BadRequestError问题求助
问题解决:Nuxt3中Whisper API无法识别WAV音频文件
问题现象
开发Nuxt3项目时,客户端通过FormData上传WAV格式音频到服务器/api/whisper路由,服务器用formidable解析后调用OpenAI Whisper API,返回400错误:Unrecognized file format. Supported formats: ['flac', 'm4a', 'mp3', 'mp4', 'mpeg', 'mpga', 'oga', 'ogg', 'wav', 'webm'],客户端显示Transcript: Internal Server Error。上传的文件确实是Whisper支持的WAV格式。
问题原因
formidable默认会将上传的文件保存为无后缀名的临时文件,而Whisper API是通过文件名后缀识别文件格式的,没有后缀的文件会被判定为不支持的格式。
解决方案
给formidable保存的临时文件添加正确后缀名,或在调用Whisper API时手动指定包含后缀的文件名,以下是两种可行方案:
方案1:修改formidable配置,保留文件后缀
修改parseForm函数,配置formidable的filename选项,让临时文件保留原文件的后缀:
async function parseForm(event: any) { return new Promise((resolve, reject) => { const form = formidable({ filename: (name, ext, part, form) => { // 保留原文件名的后缀 return `${Date.now()}${ext}`; } }); form.parse(event.req, (err, fields, files) => { if (err) reject(err) resolve({ fields, files }) }) }) }
方案2:调用Whisper API时指定文件名
无需修改formidable配置,给可读流手动添加name属性(包含后缀),OpenAI SDK会读取该属性识别文件格式:
// 替换原转录调用代码 const fileStream = fs.createReadStream(filePath); // 手动添加带后缀的文件名 Object.defineProperty(fileStream, 'name', { value: audioFile[0].originalFilename }); const transcript = await openAI.audio.transcriptions.create({ model: "whisper-1", file: fileStream, language: "ja" })
修改后的完整服务器代码(方案2示例)
import OpenAI from "openai"; import formidable from 'formidable'; import fs from 'fs'; const openAI = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }) export default defineEventHandler(async (event) => { try { const { fields, files } = await parseForm(event); const audioFile = files.audio if (!audioFile) throw new Error('Audio file is missing') const filePath = audioFile[0].filepath if (!filePath) { console.error('ファイルパスが未定義です。') return { status: 400, body: 'File path is undefined' } } // 创建可读流并添加文件名属性 const fileStream = fs.createReadStream(filePath); Object.defineProperty(fileStream, 'name', { value: audioFile[0].originalFilename }); const transcript = await openAI.audio.transcriptions.create({ model: "whisper-1", file: fileStream, language: "ja" }) return { status: 200, body: transcript.text // 转录文本在text属性中,直接返回便于前端处理 } } catch (error) { console.error(error) return { status: 500, body: "Internal Server Error" } } }) async function parseForm(event: any) { return new Promise((resolve, reject) => { const form = formidable(); form.parse(event.req, (err, fields, files) => { if (err) reject(err) resolve({ fields, files }) }) }) }
额外注意点
- 客户端代码中,
transcript.value = res.body需注意:服务器返回的transcript.text才是转录的纯文本内容,原代码返回整个transcript对象可能导致前端显示异常。 - 可在客户端添加文件格式校验,仅允许选择Whisper支持的格式,减少无效请求。
内容的提问来源于stack exchange,提问作者umi tori
相关产品推荐
相关产品推荐

