- How to preload file via Carrierwave for api?
- How to preload huge file before sending all form?
Carrierwave doesn't give approach for preloading file, so there is simple implementation.
# config/initializers/carrierwave.rb | |
ActiveRecord::Base.extend Carrierwave::PreloadAdapter |
# app/models/document.rb | |
class Document < ApplicationRecord | |
mount_preload_uploader :file, FileUploader | |
mount_preload_uploaders :files, FileUploader | |
end |
# lib/carrierwave/preload_adapter.rb | |
module Carrierwave | |
module PreloadAdapter | |
def mount_preload_uploader(attribute, uploader_class, **options) | |
mount_uploader attribute, uploader_class, options | |
define_method "#{attribute}=" do |data| | |
uploader = PreloadUploader.build_preloaded(data) | |
return super(data) if uploader.blank? | |
@preload_uploaders ||= [] | |
@preload_uploaders << uploader | |
super(uploader.file) | |
end | |
after_save { [*@preload_uploaders].compact.each(&:remove!) } | |
end | |
def mount_preload_uploaders(attribute, uploader_class, **options) | |
mount_uploaders attribute, uploader_class, options | |
define_method "#{attribute}=" do |data| | |
uploaders = data.map { |el| PreloadUploader.build_preloaded(el) }.compact | |
return super(data) if uploaders.blank? | |
@preload_uploaders ||= [] | |
@preload_uploaders += uploaders | |
super(uploaders.map(&:file)) | |
end | |
after_save { [*@preload_uploaders].compact.each(&:remove!) } | |
end | |
end | |
end |
# app/uploaders/preload_uploader.rb | |
class PreloadUploader < CarrierWave::Uploader::Base | |
OUTCOME_PREFIX = 'preload'.freeze | |
TIMESTAMP_FORMAT = '%Y%m%d-%H%M%-s'.freeze | |
storage :file | |
def self.build_preloaded(data) | |
return unless data =~ %r{#{OUTCOME_PREFIX}:(\d{8}-\d{14})/(.*)} | |
folder_name = Regexp.last_match[1] | |
file_name = Regexp.last_match[2] | |
instance = new(folder_name) | |
instance.retrieve_from_store!(file_name) | |
instance | |
end | |
def initialize(folder_name = Time.current.strftime(TIMESTAMP_FORMAT)) | |
@folder_name = folder_name | |
end | |
def store_dir | |
"preloading/#{@folder_name}" | |
end | |
def move_to_cache | |
false | |
end | |
def outcome | |
"#{OUTCOME_PREFIX}:#{@folder_name}/#{filename}" | |
end | |
end |
Rails.application.routes.draw do | |
resources :uploads do | |
post :preload, on: :collection | |
end | |
end |
# app/controllers/uploads_controller.rb | |
class UploadsController < ApplicationController | |
def preload | |
uploader = PreloadUploader.new | |
uploader.store!(params[:file]) | |
if uploader.file.present? | |
render json: { code: :ok, result: uploader.outcome } | |
else | |
render json: { code: :error }, status: :unprocessable_entity | |
end | |
end | |
end |