First part of the blog was about setting up your Rails backend for being able to attach photo to a user. Here is a link in case if you missed.
This blog is going to be mostly about Front end of the App. I am skipping the creation of React App and User Profile Component (I assume you already have it). I am going to stick to a topic of this blog and start straight with Photo Uploader Component. Here is my render method, which return:
- Form for selecting an image file from your local machine,
- Image preview,
- Upload button
Attribute type
with value "file"
allows user to select and input a file. The accept
attribute can only be used with <input type=”file”> and specify the allowed file type. So if you want the user upload only JPEGs and GIFs file types set corresponding value: accept="image/gif, image/jpeg"
OR accept=".gif,.jpeg"
It work both ways. accept="image/*"
allows user to upload any type of images.
Explanation onChange, onSubmit events and filePreview() method.
changeHandler
method set the selected by user file in the component state.
filePreview
method displays the selected image thumbnail. For implementing this I used URL.createObjectURL() method provided by the Web API . The method creates a DOM URL representing the object given in the parameter. Here is the result I got, great image preview!
Useful tip! “Choose files” text on the button is pre-defined by the browser and can’t be changed. But there is a way to hide the file name using CSS:
input[type='file'] {color: transparent;}
And lastly onSubmit
event envokes protoUploader
method.
protoUploader
method creates a formData object with the key and value pair. In my example the key is “photo”, you can name it whatever. The value is the selected file that stored in the Component State. formData()
method primary used to capture HTML form input as object and send it using HttpRequest. Ensure not to include "Content-Type' : "application/json
to the header of request, it will cause a bug !!!
Going back to the Rails backend.
Now we need to go back to the backend and add a custom route and a method for posting the image.
In config/routes.rb:
post '/users/:id/upload_photo', to: 'users#upload_photo’
In controllers/users_controller.rb:
This #upload_photo
method attaches the file to the User using Active Storage method .attach()
For debugging purpose to access your attachment in rails console rails c
use the following methods:User.last.photo.attached?
- to check if the image was successfully attachedUser.last.photo.blob
to access the attachment
Retrieve the image from Cloud and render it in the app I need to know the Url. For implementing that I can let User Serializer know that URL for attached photo should be always rendered with the user object ! That way I can simply use that Url and pass it as src
in the <img> for rendering user image. However, there are few options what exactly might be rendered:
- Url of the image on localhost
- Url of the image on Cloudinary
- The blob.key (which is the image id on Cloudinary).
There are 3 lines of code for each option in my #photo
method. If you choose first or the second option simply use those commented lines of code. For the second option you also need to have get_photo_url() method in User Model and add include Rails.application.routes.url_helpers
because url_for() is a helper method.
But !!!! I decided to go with a third option and get a blob.key
with my fetch response. The reason behind that is because by default Cloudinary uses the blob.key
as the id of the image.
You can check it in your rails console:User.last.photo.blob.key.
The key will be the same as the id of the last image on Cloudinary. I need to have the access to those id’s on Cloudinary in order to transform and manage images.
To retrieve transformed images from Cloudinary you need to add some parameters in the url, so if my #photo
method in User Serializer would return image url it might get complicated to modify that url in a right way. Cloudinary has a library that generate those url’s, you only need to specify your cloud name and the image id (which is a blob.key). That is exactly why in my #photo
method I decided to return a blob.key and not the whole url.( If you are not planning to transform user images and simply need to render them in original way, returning Url in #photo
method is the ideal option, you don't need to install a Cloudinary library, just simply pass the URL from fetch response to the <img src=” URL”> in your component and it will render an original user picture in your App.
In the next part of this blog I am going to install Cloudinary Library to my React Аpp and show how to work with image transformation and how to render circled avatars in the App.