Search code examples
carrierwavefog

Delayed job to check if a carrierwave_direct upload has been completed and attached to a model


I'm using carrierwave_direct and delayed_job with user uploaded images. The process is:

  1. User drag & drops or selects a file.
  2. File is uploaded to S3 directly using carrierwave_direct to do all the form signing etc.
  3. User is presented with a dialog to crop their image, the key (from carrierwave_direct) is passed in through a hidden field. At this point no record exists in the database, but a file is stored on S3. Should the user now move away from the page, we will have an abandoned / unattached file on S3 that does not belong to any model.
  4. If user completes the process, a record will be created and processing (cropping and creating several thumbnail versions) is done by a worker queue.

I had to make a delayed job that is put into the queue as soon as the file is uploaded. It will have a delay of several hours (or days). This job checks to see if the file exists, then checks through all recently created image records in the database to see if any are attached to that file. If not, it deletes the file.

Lastly, I wanted to make use of Carrierwave classes to handle all the Fog stuff, since I was using it anyway.

I could not find this anywhere, so here is my version. Leaving this here for people to come across in the future.


Solution

  • This is how I did it.

    def new
        # STEP 1: Show upload dialog that user can drop an image unto
        @image = current_user.portfolio.images.new.images
    
        # Status 201 returns an XML
        @image.success_action_status = "201"
    end
    
    def crop
        # STEP 2: A file is now on Amazon S3, but no record exists in the database yet. Make the user crop the image and enter a title.
        # Meanwhile, also setup a delayed job that will later check if this step has been completed.
        # Note: the crop view gets retrieved and inserted using ajax in images.js.coffee
        @image = Image.new(key: params[:key])
        Delayed::Job.enqueue ImageDeleteIfUnattachedJob.new(params[:key]), 0, 15.minute.from_now.getutc
    
        render :partial => "images/crop.html.erb", :object => @image
    end
    
    ImageDeleteIfUnattachedJob = Struct.new(:key) do
      def perform
        # Do any of the images created in the last week match the key that was passed in?
        # If not, the user probably went through the upload process, which then either went wrong or was cancelled.
        unless Image.where("created_at > ?", 1.week.ago).order('created_at DESC').any? { |image| key == image.images.path }
    
            # We spawn these to let Carrierwave handle the Fog stuff.
            @uploader = ImageUploader.new
            @storage = CarrierWave::Storage::Fog.new(@uploader)
            @file = CarrierWave::Storage::Fog::File.new(@uploader, @storage, key)
            if @file.exists?
                # Indeed, the file is still there. Destroy it.
                @file.delete
            else
                return true
            end
        end
      end
    
      def max_attempts
        3
      end
    end