Image and file uploading from a client to a server is a really typical thing you'll need to setup in a web app. Here is a quick implementation guide on how to achieve this with React and Rails.
For some context the app allows users to add dog's with a name and image.
-
Create a component that renders a form with an input file and submit, something like this, note that when the inputs change new states are set:
import React from "react"; const [file, setFile] = useState(""); const [name, setName] = useState(""); function App() { return ( <form> <label htmlFor="name">Name</label> <input type="text" name="name" id="name" value={name} onChange={(e) => setName(e.target.value)} /> <label htmlFor="file">Dog image</label> <input type="file" name="file" id="file" onChange={(e) => { setFile(e.target.files[0]); }} /> </form> ); }
-
Set the encType attribute of the form to be
multipart/form-data
, this ensures the fetch request is sent with this content type that is needed for files, also add an onSubmit event listener with a function that is bound to it<form encType="multipart/form-data" onSubmit={onFormSubmit}></form>
-
Configure the
onFormSubmit
function, this function creates a form data object that is sent in the body of the request, we have to use form data here as we're dealing with files, JSON wouldn't work, we make a POST request to an endpoint we'll configure soon in rails and weawait
its responseasync function onFormSubmit(e) { e.preventDefault(); const formData = new FormData(); formData.append("file", file); formData.append("name", name); try { const response = await fetch("http://localhost:3000/dogs", { method: "POST", body: formData, }); console.log(response); } catch (err) { console.log(err); } }
-
Shifting to rails create the db and the
Dog
model that we need:rails db:create rails g model Dog name rails db:migrate
-
Install active storage
bin/rails active_storage:install
-
Add the active storage relation to the Dog model
has_one_attached :image
-
Create a dogs controller
rails g controller dogs
-
Add an index and create action to routes
resources :dogs, only: [:index, :create]
-
Add the controller actions we need to the dogs_controller, we use the
url_for
method to extract the url from the active storage relationclass DogsController < ApplicationController def index dogs = Dog.all.map do |dog| { name: dog.name, image_url: url_for(dog.image) } end render json: dogs end def create dog = Dog.new(name: params[:name]) if dog.save dog.image.attach(params[:file]) render status: :ok else render status: :bad_request end end end
-
Deal with cors, uncomment
rack-cors
from your gemfile and whitelist your client side url in thecors.rb
file -
Your ready to now add an image and submit it on the client
-
To see all of your dogs in an array you could open postman and make a
GET
request tohttp://localhost:3000/dogs