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/activestorageNow 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
endimport 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
Usermodel withavatarassociation. Please make sure to update the hidden inputnameattribute. - If multiple files are attached, then direct upload all the files and generate the
ActiveStorage::Bloband send it with theform_dataon form submit. - The
form_dataon form submit should haveavatar: <blob.signed_id>value to associate the uploaded file in the database.
Happy Coding!!