Shrine Direct S3 Upload results in octet-stream mime-type

134 Views Asked by At

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_type into account in its file_data column, and instead, labels it as application/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...

0

There are 0 best solutions below