Jira插件向自定义后端上传文件时form-data缺失问题
Jira Forge插件文件上传form-data丢失问题解决
问题背景
开发基于Forge和自定义UI的Jira文件上传插件,自有后端采用S3+Multer处理文件上传逻辑。Postman测试后端接口完全正常,但插件内发起请求时,请求未携带form-data;而插件外的独立React组件测试相同上传逻辑可正常工作。
代码示例
import React, { useState, useRef } from 'react'; import { Button } from 'react-bootstrap'; import Plus from '../../../../../assets/icons/Plus'; import { invokeApiRemote } from '../../../../../services/invokeApi'; import { createAttachment } from '../../../../../services/apiConfig'; const FileUpload = ({ onUpload, enableDragAndDrop, entityType, entityId }) => { const [isDragging, setIsDragging] = useState(false); const [error, setError] = useState(null); const fileInputRef = useRef(null); const handleDragOver = (e) => { e.preventDefault(); setIsDragging(true); }; const handleDragLeave = () => { setIsDragging(false); }; const handleDrop = (e) => { e.preventDefault(); setIsDragging(false); const files = Array.from(e.dataTransfer.files); handleFiles(files); }; const handleFiles = async (files) => { console.log(files) const formData = new FormData(); console.log('Files to upload:', files); files && files.map((file) => { formData.append('files', file); }); formData.append('attachableType', entityType); formData.append('attachableId', entityId); console.log(formData) try { const response = await invokeApiRemote({ config: createAttachment(), body: formData, headers: { 'X-Atlassian-Token': 'no-check', }, }); if (response) { console.log('Attachment created successfully:', response); // onUpload && onUpload(response); } } catch (error) { console.error('Error creating attachment:', error); setError(error.message); } }; const handleFileChange = (e) => { const files = Array.from(e.target.files); handleFiles(files); }; const handleChooseFiles = () => { fileInputRef.current.click(); }; return ( <div> {enableDragAndDrop ? ( <div onDragOver={handleDragOver} onDragLeave={handleDragLeave} onDrop={handleDrop} onClick={handleChooseFiles} style={{ border: isDragging ? '2px dashed #007bff' : '2px dashed #cccccc', padding: '20px', textAlign: 'center', marginBottom: '20px', cursor: 'pointer', backgroundColor: isDragging ? '#f8f9fa' : 'transparent', }}> <p>Drag & drop or click to select files</p> </div> ) : ( <Button size="sm" variant="" className="me-2" onClick={handleChooseFiles}> <Plus /> Attach files </Button> )} <input type="file" multiple onChange={handleFileChange} style={{ display: 'none' }} ref={fileInputRef} /> {error && <p style={{ color: 'red' }}>{error}</p>} </div> ); }; export default FileUpload;
问题原因
核心问题出在Forge的API调用工具invokeApiRemote的处理逻辑上:
- Forge的网络请求框架可能会自动对请求body进行JSON序列化,导致FormData被转换为无效格式或丢失。
- 若手动设置
Content-Type: multipart/form-data,会覆盖浏览器自动生成的带boundary的正确头部,导致后端无法解析form-data。 - 可能存在manifest权限配置缺失,导致请求被拦截或篡改。
解决方案
1. 避免手动设置Content-Type
移除请求中手动添加的Content-Type头部,让浏览器自动生成包含boundary的正确multipart/form-data头部:
// 修改handleFiles中的请求部分 try { const response = await invokeApiRemote({ config: createAttachment(), body: formData, headers: { 'X-Atlassian-Token': 'no-check', // 不要添加 'Content-Type': 'multipart/form-data' }, });
2. 检查invokeApiRemote实现
确保invokeApiRemote内部直接传递FormData,不对其进行JSON序列化。如果使用Forge原生request方法,需配置正确:
// 示例invokeApiRemote正确实现 export async function invokeApiRemote({ config, body, headers }) { return await request(config.url, { method: config.method, body: body, // 直接传递FormData,不要用JSON.stringify headers: { ...headers, // 禁止在这里强制设置Content-Type }, }); }
3. 验证Manifest权限配置
在manifest.yml中确保已配置外部后端的请求权限:
permissions: external: fetch: - 'https://your-backend-domain.com/**'
验证
修改后重新打包部署插件,测试文件上传功能,检查请求是否正确携带form-data,后端是否能正常接收并处理文件。
内容的提问来源于stack exchange,提问作者Adesh Kumar
相关产品推荐
相关产品推荐

