skip to Main Content

I am trying to upload and send a simple image file to my Rails 7 API using FormData from React as I read it is only right to use that while uploading a file.
this is what my code looks like

 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 

the other field hit the rails console with values except the image_field which shows an empty object in the rails console (when I try to debug I see an image object in the React debugging). I would love to know if I’m doing something wrong and why the image_field sends an empty image object to my Rails app.

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;
});

this is the rails controller action

 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

asset modal


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: I did not detail the rails setup as I strongly believe the issue is from the React code

2

Answers


  1. The problem in your React code is this header:

    'Content-type': 'application/json'
    

    You’re transmitting the data in JSON format, which is the incorrect format when handling file uploads. What you should be using is this encoding type:

    'Content-type': 'multipart/form-data'
    

    You can set this in your fetch config, BUT the recommended way to do it is to declare it in the form element via the enctype attribute:

    <form
      className={toggleForm && 'hide-form'}
      enctype="multipart/form-data"
      method="post"
      onSubmit={handleSubmit}
    >
      <!-- Elements here -->
    </form>
    

    To quote from the MDN docs:

    Warning: When using FormData to submit POST requests using XMLHttpRequest or the Fetch API with the multipart/form-data content type (e.g. when uploading files and blobs to the server), do not explicitly set the Content-Type header on the request. Doing so will prevent the browser from being able to set the Content-Type header with the boundary expression it will use to delimit form fields in the request body.

    Here’s your updated fetch logic:

    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;
    });
    
    Login or Signup to reply.
  2. What if

    • you use multipart/form-data as content-type for your request?
    • you pass your body using FormData?
    const formData = new FormData()
    formData.append('post[name]', name)
    formData.append('post[image]', image)
    
    // set `body: formData` in your request
    

    Reference:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search