使用Node.js上传文件至Crowdin Storage失败,请求返回400状态码
问题:Node.js上传文件到Crowdin Storage返回400错误(streamIsEmpty)
问题描述
上传文件至Crowdin Storage时,收到400状态码错误,提示流为空。
错误信息
{ "errors": [ { "error": { "key": "fileStream", "errors": [ { "code": "streamIsEmpty", "message": "Stream size is null. Not empty content expected" } ] } } ] }
已尝试的操作
- 使用Axios发起HTTP请求
- 上传前验证文件存在且非空
- 按Crowdin API文档设置必要请求头(Authorization、Crowdin-API-FileName、Content-Type)
- 使用正确的key将文件添加至FormData
代码片段
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const path = require('path'); // Replace with your actual Crowdin API key and project ID const CROWDIN_API_KEY = 'crowdin_api_key'; const CROWDIN_PROJECT_ID = 123467890; // Function to upload file to Crowdin storage const uploadFileToStorage = async (filePath) => { try { const fileName = path.basename(filePath); // Check if file exists and is not empty if (!fs.existsSync(filePath)) { throw new Error(`File does not exist: ${filePath}`); } const stats = fs.statSync(filePath); if (stats.size === 0) { throw new Error(`File is empty: ${filePath}`); } const form = new FormData(); const fileStream = fs.createReadStream(filePath); form.append('file', fileStream); console.log('Uploading file to Crowdin:', fileName); const response = await axios.post('https://api.crowdin.com/api/v2/storages', form, { headers: { 'Authorization': `Bearer ${CROWDIN_API_KEY}`, 'Crowdin-API-FileName': fileName, ...form.getHeaders() } }); return response.data.data.id; } catch (error) { console.error('Error uploading file to storage:', error.response ? error.response.data : error.message); if (error.response) { console.error('Response status:', error.response.status); console.error('Response data:', JSON.stringify(error.response.data, null, 2)); } throw error; } }; // Function to add file to Crowdin project const addFileToProject = async (storageId, fileName) => { try { const response = await axios.post(`https://api.crowdin.com/api/v2/projects/${CROWDIN_PROJECT_ID}/files`, { storageId: storageId, name: fileName }, { headers: { 'Authorization': `Bearer ${CROWDIN_API_KEY}`, 'Content-Type': 'application/json' } }); console.log('File added to project successfully:', response.data); } catch (error) { console.error('Error adding file to project:', error.response ? error.response.data : error.message); if (error.response) { console.error('Response status:', error.response.status); console.error('Response data:', JSON.stringify(error.response.data, null, 2)); } throw error; } }; // Main function to upload file to Crowdin const uploadFileToCrowdin = async (filePath) => { try { const storageId = await uploadFileToStorage(filePath); const fileName = path.basename(filePath); await addFileToProject(storageId, fileName); } catch (error) { console.error('Error processing the file:', error.message); } }; // Get the file path from command line arguments and call the upload function const filePath = process.argv[2]; if (!filePath) { console.error('Please provide the file path as an argument.'); process.exit(1); } uploadFileToCrowdin(filePath);
补充信息
- API key可正常通过Crowdin API认证
- Postman测试Crowdin API端点功能正常
解决思路与建议
1. 显式传递文件大小到FormData
fs.createReadStream生成的流不会自动携带文件大小信息,Crowdin API可能需要此信息验证流的有效性。修改form.append代码,传入已知的文件大小:
form.append('file', fileStream, { filename: fileName, knownLength: stats.size });
2. 确保请求头正确合并
避免axios headers合并时的潜在冲突,先单独获取FormData的headers,再合并自定义头:
const formHeaders = form.getHeaders(); const headers = { 'Authorization': `Bearer ${CROWDIN_API_KEY}`, 'Crowdin-API-FileName': fileName, ...formHeaders }; const response = await axios.post('https://api.crowdin.com/api/v2/storages', form, { headers });
3. 尝试读取文件内容而非使用流(适合小文件)
如果文件体积不大,直接读取文件内容传入FormData,避免流处理的问题:
const fileContent = fs.readFileSync(filePath); form.append('file', fileContent, fileName);
4. 检查流的编码与状态
创建流时指定编码,确保流处于可读状态:
const fileStream = fs.createReadStream(filePath, { encoding: 'binary' });
内容的提问来源于stack exchange,提问作者Santhosh Kumar O
相关产品推荐
相关产品推荐

