Search code examples
ruby-on-railsamazon-s3ruby-on-rails-5carrierwaverails-activestorage

How to implement attachment versioning for files with same names? RoR 5.2


Project based on RoR 5.2. For attachments uploading I use Carrierwave gem and AWS S3 bucket as storage.

I need to implement versioning for uploaded files with same names.

I mean that file with the same name should not be replaced when I will upload a new one. For example: model User has a mounted uploader for documents. If I will upload file with name «doc.docx» and after I will upload one else file for the same model object and with the same name, old file will be replaced with new one.

I have already investigated that I can generate token or add timestamp to get unique filename, but I need that if I will upload the new file with same name (for example 'doc.docx') for the same object, it shoild be renamed to 'doc(1).docx' or 'doc_1.docx'. And the new one will be renamed to 'doc(2).docx' or 'doc_2.docx', etc.

Versioning in the AWS panel for this bucket has already enabled.

Is there any way to achieve this? I'm ready to change from Carrierwave to Active Storage. But I have not found at least one case for this.

Any suggestions? Thanks in advance!


Solution

  • I have some ideas about renaming. At first, we can split your problem:

    1. Problem with file renaming
    2. Problem with file versioning

    I solved my problem with renaming with similar code (Carrierwave + video processing). Here I created thumbnail version for an uploaded video, with a specific name (write your custom function instead of png_name, you can check a number of previously uploaded files and etc):

    # Create different versions of your uploaded files:
    # ffmpegthumbnailer
    version :thumb do
      process thumbnail: [{ format: 'png', size: 360, logger: Rails.logger }]
      def full_filename for_file
        png_name for_file, version_name
      end
    end
    
    version :preview do
      process thumbnail: [{ format: 'png', size: 600, logger: Rails.logger }]
      def full_filename for_file
        png_name for_file, version_name
      end
    end
    
    def png_name(for_file, version_name)
      %Q{#{version_name}_#{for_file.chomp(File.extname(for_file))}.png}
    end
    

    And you can solve the problem with versioning by creating proxy-model. For instance, you have User model, than you create an association called Document. You can mount your uploader to the Document model, and use code above for the renaming.

    class User < ApplicationRecord
      has_many :documents, dependent: :destroy
    
      def last_document
        documents.last
      end
    end
    
    class Document < ApplicationRecord
      mount_uploader :file, DocumentUploader
      validates_integrity_of :file
    end