I am trying to use Ruby On Rails as my backend, and gems like ReactOnRails and Shrine, in addition, I use package Uppy to upload the image to AWS S3
So far everything working fine until I trying to upload the image through Uppy which should transfer the AWS credentials through the backend, so I don't expose my secret keys from the front end.
A snippet of React Component
.use(AwsS3, {
limit: 5,
// serverUrl: 'https://uppy-companion.my-app.com/',
strings: {
preparingUpload: 'Preparing upload...'
},
getUploadParameters(file) {
return fetch('/presign?filename=' + file.name, {
method: 'post',
// Send and receive JSON.
headers: {
accept: 'application/json',
'content-type': 'application/json'
},
body: JSON.stringify({
filename: file.name,
contentType: file.type
})
}).then((response) => {
// Parse the JSON response.
return response.json()
}).then((data) => {
// Return an object in the correct shape.
return {
method: data.method,
url: data.url,
fields: data.fields
}
}).catch((error) => console.log(error))
}
});
My Routes are
mount Shrine.presign_endpoint(:cache) => '/presign'
and finally my shrine.rb file
require 'shrine'
if Rails.env.production?
require 'shrine/storage/s3'
s3_options = {
access_key_id: Rails.application.credentials.aws[:s3_access_key_id],
secret_access_key: Rails.application.credentials.aws[:s3_secret_access_key],
region: Rails.application.credentials.aws[:s3_region],
bucket: Rails.application.credentials.aws[:s3_bucket]
}
Shrine.storages = {
cache: Shrine::Storage::S3.new(prefix: 'cache', **s3_options),
store: Shrine::Storage::S3.new(prefix: "store", **s3_options)
}
else
require 'shrine/storage/file_system'
Shrine.storages = {
cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'),
store: Shrine::Storage::FileSystem.new('public', prefix: 'uploads')
}
end
Shrine.plugin :activerecord
Shrine.plugin :backgrounding
Shrine.plugin :logging
Shrine.plugin :determine_mime_type
Shrine.plugin :cached_attachment_data
Shrine.plugin :restore_cached_data
#TODO: docs
Shrine.plugin :presign_endpoint if Rails.env.production?
Shrine.plugin :upload_endpoint if !Rails.env.production?
Shrine::Attacher.promote { |data| PromoteJob.perform_async(data) }
Shrine::Attacher.delete { |data| DeleteJob.perform_async(data) }
my AWS S3 Cros configurations
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
<AllowedHeader>x-amz-date</AllowedHeader>
<AllowedHeader>x-amz-content-sha256</AllowedHeader>
<AllowedHeader>content-type</AllowedHeader>
</CORSRule>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration>
I just cannot figure out how to fix this issue when I upload file through the uppy.js.
Shrine's presign endpoint responds to GET
requests, not POST
, so you need to do
getUploadParameters(file) {
return fetch('/presign?filename=' + file.name, {
method: 'get',
...
By the way, if you mount the presign endpoint on /s3/params
:
Rails.application.routes.draw do
mount Shrine.presign_endpoint(:cache) => "/s3/params"
end
you don't need any of this fetch()
logic, you can just point Uppy to your app, and it will automatically use /s3/params
and do all of the fetching work for you:
uppy.use(AwsS3, {
companionUrl: '/',
...
})