I recently tried out using Rails 6's new Action Text feature for a simple blog page. One thing I like is about it is how it lets you drag and drop in multiple images and then automatically places them into a nicely formatted "gallery" layout.
When images are placed into the Action Text editor, it performs a direct upload of each image into your storage location via JavaScript. Depending on what you are using for image storage, you may run into some problems caused by CORS (Cross-origin resource sharing) errors.
In my case, I had this problem with Google Cloud Storage. Every time Rails tried to upload a photo through the Action Text edit, I was getting a No 'Access-Control-Allow-Origin' header is present on the requested resource
CORS error. This is because the browser won't allow the sharing of resources from two different origins, e.g. my-app.com
and my-app.storage.googleapis.com
, including sending POST
requests when trying to upload a photo to storage. (See more on Google Cloud's CORS page).
It took me quite a while to figure out how to fix this, so I wanted to share what I did here!
To solve this problem, you need to modify the CORS configuration for your Google Cloud Storage bucket, which can be done using something called the "gsutil" tool.
The gsutil tool is a CLI that lets you manage Google Cloud Storage buckets from your command line. If you don't have it installed, do so by following Google's installation instructions. You'll also need to login to Google Cloud through the command line with gcloud init
(also explained on the installation page) or else you won't be able to configure your bucket.
Now you can get to configuring your CORS settings!
To check what the current CORS configuration, use the following command, replacing my-bucket-name
with the actual name of your bucket.
gsutil cors get gs://my-bucket-name
This will return some JSON that looks something like this:
[
{
"origin": ["http://example.appspot.com"],
"responseHeader": ["Content-Type"],
"method": ["GET", "HEAD", "DELETE"],
"maxAgeSeconds": 3600
}
]
To change/update the CORS configuration, you will first need to create a JSON file that contains your configuration settings. You can name the file whatever you want, but I went with cors.json
. You can also technically save it anywhere on your computer, but I decided to put mine into my Rails app's config
directory so I could easily keep track of it and update it if needed.
Now, to solve the problem happening when Action Text attempts to upload images directly to your bucket, you will need to make sure that URL of your app is listed in the JSON's origin
array, added the Content-MD5
content type to the responseHeader
array, and add the PUT
, POST
, and OPTIONS
methods into the method
array.
The finished cors.json
file should look like this:
// cors.json
[
{
"origin": ["https://my-app.com"],
"responseHeader": ["Content-Type", "Content-Md5"],
"method": ["PUT", "GET", "HEAD", "DELETE", "OPTIONS"],
"maxAgeSeconds": 3600
}
]
To configure the bucket with these settings, use the following command:
gsutil cors set cors.json gs://my-bucket-name
Note that the cors.json
in the above command refers to the location/path of the file. Either cd
into the same directory or write out the path. (E.g. I had mine in my config/
directory, so I used gsutil cors set config/cors.json gs://my-bucket-name
from the app's root directory.
You can check if the CORS configuration was updated by using the gsutil cors get
command again:
gsutil cors get gs://my-bucket-name
Note that you may still get CORS errors after updating because the browser may have cached the previous settings. If so, try completely clearing the cache and refreshing the page. Another thing you can try is temporarily setting the "maxAgeSeconds"
in the cors.json
to something super short like 1
so that the browser refreshes the response settings immediately (Make sure to change it back later).
If you still have problems, you may need to do some troubleshooting by actually inspecting the response headers using Chrome Developer Tools. Google's troubleshooting documentation gives you a good overview of what to look out for.
You can inspect the response headers by opening Dev Tools and navigating to the Network
tab. Try uploading another image and then find the request to upload the image within the list of network activity. Click the name of the request Name
column and check the Headers
section (you may have to check a few different requests before you find the right one). You'll know it's the right one if the Request URL
starts with https://storage.googleapis.com/.....
.
Here you want to make sure that the Origin
within the Request Headers matches exactly one of the origins in your cors.json
. Also make sure that the Access-Control-Request-Method
(e.g. content-md5
) and Access-Control-Request-Method
(e.g. PUT
or OPTIONS
) matches what is in the JSON file. If not, update the JSON file accordingly.
For example, I had a typo in the URL listed for the origin in my cors.json
file and had to update it. I also noticed later that I had to add the OPTIONS
method to method
array (I had only listed PUT
).
Happy uploading!
Top comments (5)
Thanks a ton for this, was a big help. I had to make a small adjustment and set the response headers slightly differently but this led me down the right path. Props.
For anyone else still sturggling this is the change I had to make:
"responseHeader": ["Origin", "Content-Type", "Content-MD5", "Content-Disposition"],
I was like two days trying to understand why I couldn't upload photos from my action text. You saved me, thanks!!!!!!!
It took me FOREVER to figure out what was going on too! I'm glad it helped :D
You saved me a lot of days. Thank you so much. ~<3
Yay, I'm glad it helped!