Search code examples
ruby-on-railsrails-activestorage

Rails Active Storage without model


Can Rails Active Storage be used without a model backing it? I have a form where I need a file to be uploaded but I don't wish it to be attached to a model. I just need the file uploaded so I can process it with a background job and then delete it.


Solution

  • Yes.

    When ActiveStorage is backed by a model, there are two parts involved:

    • An ActiveStorage::Blob record that holds the file info in the active_storage_blobs table
    • An ActiveStorage::Attachment record that connects the blob to the model using the active_storage_attachments table

    You can skip creating the ActiveStorage::Attachment record if you call create_and_upload! directly. This will create a unique key, determine the content type, compute a checksum, store an entry in active_storage_blobs, and upload the file:

    filename = 'local_file.txt'
    file = File.open(filename)
    blob = ActiveStorage::Blob.create_and_upload!(io: file, filename: filename)
    

    You can download later with:

    blob.download
    

    And delete with:

    blob.purge
    

    If you want to skip the storage of the ActiveStorage::Blob entirely, you would need to go directly to the storage service that manages uploading and downloading files. For example, if you are using Disk storage you'd see something like this:

    ActiveStorage::Blob.service
     => #<ActiveStorage::Service::DiskService ...
    

    Then you'd have to generate your own key and do something like:

    service = ActiveStorage::Blob.service
    key = 'some_unique_key'
    service.upload(key, file)
    service.download(key)
    service.delete(key)
    

    To upload a file without a model you can use form_with and specify a url like:

    <%= form_with url: "/uploads", multipart: true do |form| %>
      <%= form.file_field :picture %>
      <%= form.submit %>
    <% end %>
    

    Then on the server you can get the file with:

    file = params[:picture]
    blob = ActiveStorage::Blob.create_and_upload!(io: file, filename: file.original_filename)
    ...