carrierwaveuploader / carrierwave

Classier solution for file uploads for Rails, Sinatra and other Ruby web frameworks
https://github.com/carrierwaveuploader/carrierwave
8.78k stars 1.66k forks source link

Files are not uploaded to S3 after reload in after_commit #2656

Closed RomanUkraine closed 1 year ago

RomanUkraine commented 1 year ago

leave.rb:

class Leave < ApplicationRecord
  mount_uploaders :files, FileUploader

  after_commit :do_something

  private

  def do_something
    self.reload --- this is where it breaks. Works as expected w/o .reload
  end
end

file_uploader.rb:

class FileUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def extension_allowlist
    %w[jpg jpeg gif png bmp doc docx txt pdf ppt pptx xls xlsx]
  end
end

leaves_controller.rb:

module Api
  module V1
    class LeavesController < Api::V1::BaseJsonapiController
      include Responsable

      before_action :set_leave, only: %i[update_files destroy_files]

      def update_files
        save_params(Api::V1::LeaveResource) do
          @leave.mute_notification = context[:user]&.admin? && @leave.employee != context[:user]
          @leave.files += params[:files]
          @leave.save!
        end
      end
   end
end

routes.rb:

  scope module: :api, defaults: { format: :json }, path: 'api' do
    scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
      jsonapi_resources :leaves do
        collection do
          get :pending_count
        end
        member do
          patch :files, action: :update_files
          delete :files, action: :destroy_files
        end
      end
    end
  end

Steps to reproduce:

  1. PATCH request to /api/leaves/:id/files

    curl -X PATCH \
    http://localhost:3005/api/leaves/218/files \
    -F 'files[]=@/users/imf/Downloads/pic3.png'
  2. Get the URL of the file Leave.find(218).files.map(&:url) => 'https://host_name.s3.amazonaws.com/uploads/leave/photo/2/pic.png'

Expected Result: Content of the uploaded file is returned

Actual Result: NoSuchKey is returned

<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
<Key>uploads/leave/photo/2/pic.png</Key>
<RequestId>RXZ1DXGCD5G5RK8K</RequestId>
<HostId>bXjt7U7CsaaswVtfdga5RnZG/adAJAMVJ45cpAYKR47Jvbjj4+hG5Hx1frkM9/yP23DOWubJ35o=</HostId>
</Error>
mshibuya commented 1 year ago

reload in after_commit

Just don't do that. CarrierWave 2.x stores attachments in the after_commit hook. It is obvious that mutating the object's state in after_commit breaks its functionality.