我正在尝试使用 React 中的 FormData 上传一个简单的图像文件并将其发送到我的 Rails 7 API,因为我读到只有在上传文件时才可以使用它。 这就是我的代码的样子
const handleSubmit = (e) => {
e.preventDefault();
let formData = new FormData(e.currentTarget);
const data = Object.fromEntries(formData)
dispatch(createAsset(data)); //this a redux function
e.currentTarget.reset()
};
<form onSubmit={handleSubmit} className={toggleForm && 'hide-form'}>
<div>
<label>Property Title</label>
<input type="text" id="name" name="name" />
</div>
<div>
<labal>image upload</labal>
<input type="file" name="image" />
</div>
<button className="btn" type="submit"> create assets</button>
</form>
HTTP request is made
另一个字段用除 image_field 之外的值命中 Rails 控制台,该字段在 Rails 控制台中显示一个空对象(当我尝试调试时,我在 React 调试中看到一个图像对象)。我很想知道我是否做错了什么以及为什么 image_field 向我的 Rails 应用程序发送一个空图像对象。
const createAsset = createAsyncThunk('asset/create_asset', async (data) => {
const response = await fetch(`${baseUrl}assets`, {
method: 'POST',
headers: {
'Content-type': 'application/json',
Authorization: `Bearer ${token()}`,
},
body: JSON.stringify(data),
}).then((res) => res.json());
return response;
});
这是 Rails 控制器的动作
def create
@asset = Asset.new(asset_params)
if @asset.save
render json: @asset, status: :created
else
render json: @asset.errors, status: :unprocessable_entity
end
end
def asset_params
params.require(:asset).permit(:name, :image)
end
资产模式
class Asset < ApplicationRecord
has_one_attached :image
def image_url
# Rails.application.routes.url_helpers.rails_blob_path(image, only_path: true)
Rails.application.routes.url_helpers.url_for(image) if image.attached?
end
end
PS:我没有详细说明 Rails 设置,因为我坚信问题来自 React 代码
React 代码中的问题是这个标头:
'Content-type': 'application/json'
您正在以 JSON 格式传输数据,这是处理文件上传时的不正确格式。您应该使用的是这种编码类型:
'Content-type': 'multipart/form-data'
您可以在
fetch
配置中进行设置,但是推荐的方法是通过 enctype 属性在表单元素中声明它:
<form
className={toggleForm && 'hide-form'}
enctype="multipart/form-data"
method="post"
onSubmit={handleSubmit}
>
<!-- Elements here -->
</form>
引用 MDN 文档:
警告: 当使用
使用FormData
或带有XMLHttpRequest
内容类型的Fetch API
提交 POST 请求时(例如,将文件和 blob 上传到服务器时),不要显式设置multipart/form-data
请求中的标头。这样做将阻止浏览器使用边界表达式设置Content-Type
标头,该标头将用于分隔请求正文中的表单字段。Content-Type
这是更新后的获取逻辑:
const createAsset = createAsyncThunk('asset/create_asset', async (data) => {
const response = await fetch(`${baseUrl}assets`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token()}`,
},
body: data, // make sure to pass the `FormData` object directly
}).then((res) => res.json());
return response;
});