After following this tutorial, we set up a Direct Upload using Shrine and Uppy on S3.
During our tests, a pdf file is well uploaded on S3 in the cache folder via a Drag&Drop box on our app. The problem is that when we assign it to our Doc model, two things happened:
- The Doc object created do not take the
mime_typeinto account in itsfile_datacolumn, and instead, labels it asapplication/octet-stream. - Consequently (I think), the cached file on S3 do not get promoted.
Our setup seems to be ok, as it used to work just a month ago. Still, we cannot figure it out after many tries and ivestigations... Please note that our S3 CORS and ENV setup is ok, and that there do not seem to be any problems with authorizations/access.
shrine.rb
require "shrine/storage/s3"
s3_options = {
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
region: ENV['AWS_REGION'],
bucket: ENV['AWS_BUCKET'],
}
Shrine.storages = {
cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options), # For direct uploads
store: Shrine::Storage::S3.new(public: true, prefix: "store", **s3_options)
}
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data
Shrine.plugin :restore_cached_data
Shrine.plugin :determine_mime_type
Shrine.plugin :backgrounding
Shrine.plugin :remove_invalid
Shrine.plugin :presign_endpoint, presign_options: lambda { |request|
filename = request.params['filename']
type = request.params['type']
{
content_disposition: "inline; filename=\"#{filename}\"",
content_type: type,
content_length_range: 0..(15 * 1024 * 1024)
}
}
file_uploader.rb
class FileUploader < Shrine
require "content_disposition"
plugin :validation
plugin :validation_helpers
plugin :derivatives
Attacher.validate do
validate_max_size 9.megabyte, message: 'Votre fichier ne doit pas dépasser 9Mo.'
end
plugin :default_storage, store: -> {
org = record.documentable_type.constantize.find(record.documentable_id).organization
if record.documentable_type == 'Organization'
org = record.documentable_type.constantize
"store_organizations/#{record.documentable_id}/documents"
elsif record.documentable_type == 'Entity'
"store_entities/#{record.documentable_id}/documents"
elsif record.documentable_type == 'Project'
"store_projects/#{record.documentable_id}/documents"
end
}
plugin :dynamic_storage
storage /store_(.+)/ do |match|
bucket_name = "#{match[1]}"
Shrine::Storage::S3.new(bucket: ENV["AWS_BUCKET"], prefix: bucket_name)
end
end
upload_controller.js (Uppy part)
[...]
this.uppy
.use(DragDrop, {
target: '.file-dropzone .for-DragDrop',
}
})
// Load to S3 cache folder -> OK
.use(AwsS3, {
companionUrl: this.urlValue,
allowedMetaFields: [
'name'
]
})
// Construct uploaded file data in the format that Shrine expects
.on('upload-success', function (file, data) {
var filename = file.name
var uploadedFileData = JSON.stringify({
id: file.meta['key'].match(/^cache\/(.+)/)[1], // object key without prefix
storage: 'cache',
metadata: {
size: file.size,
filename: file.name,
mime_type: "application/pdf" // file.type
}
})
// docs_controller#direct_upload
fetch(postUrl, {
method: "POST",
credentials: "same-origin",
body: JSON.stringify({
filename: filename,
file: uploadedFileData,
}),
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content,
'Content-Type': 'application/json'
},
})
})
[...]
doc.rb
class Doc < ApplicationRecord
include ActionView::Helpers::TagHelper
include FileUploader::Attachment.new(:file)
end
docs_controller.rb
[...]
def direct_upload
# binding.pry
@doc = Doc.new(
name: params[:filename],
file: params[:file],
)
end
[...]
Many thanks for your help!
The doc_controller binding.pry leads to those params[:file]: "{\"id\":\"dd5acef2cd5f7303309406308c849720.pdf\",\"storage\":\"cache\",\"metadata\":{\"size\":1180288,\"filename\":\"Demande + certificat.pdf\",\"mime_type\":\"application/pdf\"}}"
But once assigned to the Doc object we got octet-stream in file_data column:
file_data: "{\"id\":\"dd5acef2cd5f7303309406308c849720.pdf\",\"storage\":\"cache\",\"metadata\":{\"size\":1180288,\"filename\":\"Demande + certificat.pdf\",\"mime_type\":\"application/octet-stream\"}}"
We are completly lost after trying all Shrine plugins, etc, etc...