I am using shrine to directly upload to aws-s3
bucket and is working fine. Now I want to use DigitalOcean spaces
instead. So I changed some settings for shrine like below(only changed the env
variables relevant to spaces).
require "shrine"
require "shrine/storage/s3"
s3_options = {
access_key_id: ENV["SPACES_ACCESS_KEY_ID"],
secret_access_key: ENV["SPACES_SECRET_ACCESS_KEY"],
region: ENV["SPACES_REGION"],
bucket: ENV["SPACES_BUCKET"],
endpoint: ENV["SPACES_ENDPOINT"]
}
Shrine.storages = {
cache: Shrine::Storage::S3.new(prefix: "cache", upload_options: {acl: "public-read"}, **s3_options),
store: Shrine::Storage::S3.new(prefix: "store", upload_options: {acl: "public-read"}, **s3_options),
}
Shrine.plugin :activerecord
Shrine.plugin :presign_endpoint
Shrine.plugin :restore_cached_data
Shrine.plugin :backgrounding
Shrine::Attacher.promote {|data| UploadJob.perform_async(data)}
Shrine::Attacher.delete {|data| DeleteJob.perform_async(data)}
I also added cors in spaces to allow all requests like below
But when I upload I am getting this error.
<Error><Code>AccessDenied</Code><Message>Policy missing condition: Content-Type</Message><BucketName>testing-dev</BucketName><RequestId>tx0000000036366363370-3663t37373-33883-sgp1a</RequestId><HostId>349494-sgp1a-sgp</HostId></Error>
What could be the issue here? I can see that the error telling content-type
missing in policy. But how can I add that if thats the case?
It seems that DigitalOcean Spaces now requires the presign policy to include contentType
condition. The presign policy is generated in the presign endpoint, so you can tell it to add :content_type
based on the filename
query parameter:
Shrine.plugin :presign_endpoint, presign_options: -> (request) do
filename = request.params["filename"]
extension = File.extname(filename)
content_type = Rack::Mime.mime_type(extension)
{ content_type: content_type }
end
This should make :content_type
always present, because if the filename
has no extension or the extension is unrecognised, Rack::Mime.mime_type
will return application/octet-stream
, which is the content type S3 assigns to objects by default anyway.
Just make sure you're passing the filename
query parameter on the client side when sending the presign request. E.g. using window.fetch
it would be:
fetch('/presign?filename=' + file.name)