将 Image 文件从 React 上传到 Rails 服务器

问题描述 投票:0回答:1

我正在尝试使用 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 代码

reactjs ruby-on-rails form-data
1个回答
0
投票

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
内容类型的
multipart/form-data
提交 POST 请求时(例如,将文件和 blob 上传到服务器时),不要显式设置
请求中的 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;
});
© www.soinside.com 2019 - 2024. All rights reserved.