Every modern web application nowadays tends to have a requirement to upload files. This feature may be for uploading an avatar for a user profile or adding attachments.
File upload in GraphQL-ruby is a tricky problem. You can do file upload using multipart form request but GraphQL doesn’t allow multipart form request. I have spent countless hours to get multipart request working in GraphQL but the solution didn’t map the files properly in Active Storage so I had to let it go.
File upload can also be done using Direct Upload to the S3. We can achieve direct upload using two ways. We’ll discuss the best and the easiest way to achieve the direct upload in this blog.
Direct Upload using @rails/activestorage
Firstly, install the Active Storage NPM package if not installed already.
yarn add @rails/activestorage
# OR
npm install @rails/activestorage
Now let’s build a simple react component to upload the profile picture. In the following example, we would consider the following example:
class User < ApplicationRecord
has_one_attached :avatar
end
import React, { Fragment, useState } from "react";
import { DirectUpload } from "@rails/activestorage";
const DIRECT_UPLOAD_URL = "/rails/active_storage/direct_uploads";
export default function UploadProfilePicture(props) {
const [blob, setBlob] = useState({});
const isBlobPresent = Object.keys(blob).length === 0;
const handleFileUpload = (event) => {
const file = event.target.files[0];
const upload = new DirectUpload(file, DIRECT_UPLOAD_URL);
upload.create((error, blob) => {
if (error) {
# Handle the error.
} else {
# Set the blob local state or in the global form state you are using.
# Add a hidden field for the associating uploaded blob to the ActiveRecord.
# Example: User has_one_attached avatar
setBlob(blob);
}
});
}
return (
<Fragment>
{
isBlobPresent ? (
# TODO: Change the name to ActiveStorage association
<input type="hidden" name="avatar" value={blob.signed_id} />
) : null
}
<input type="file" accept="images/*" />
</Fragment>
)
}
In the above component you need to take care of the following things:
- We have considered the
User
model withavatar
association. Please make sure to update the hidden inputname
attribute. - If multiple files are attached, then direct upload all the files and generate the
ActiveStorage::Blob
and send it with theform_data
on form submit. - The
form_data
on form submit should haveavatar: <blob.signed_id>
value to associate the uploaded file in the database.
Happy Coding!!