Rails Active Storage - 如何将本地文件迁移到 s3 存储桶

问题描述 投票:0回答:7

之前我的文件上传到存储文件夹中。但现在我想在 s3 存储桶上上传图像。如何迁移 s3 存储桶上现有的本地数据?

我在这里找到了脚本https://www.stefanwiert.de/blog/2018/11/05/active-storage-migrate- Between-providers-from-local-to-amazon/ 但出现错误

NoMethodError(为 Active Storage 调用私有方法“open”

那么我应该如何将本地数据迁移到 s3 存储桶呢?

有没有更简单的方法?

ruby-on-rails amazon-s3 rails-activestorage
7个回答
6
投票

根据 Dorian 的回答,我构建了一个更简单的版本。对于此版本,您不需要选择类或知道/关心如何调用类内的方法。

应该也适用于

has_many_attached
(因为我们从附件本身开始)

就像 Dorian 的版本一样,您需要在此之前添加 s3 的配置并部署它

ActiveStorage::Attachment.find_each do |at|
  next unless at.blob.service_name == "local"
  begin
    blob = at.blob
    blob.open do |f|
      at.record.send(at.name).attach(io: f, content_type: blob.content_type, filename: blob.filename)
    end
    blob.destroy
  rescue ActiveStorage::FileNotFoundError
    # Add some message or warning here if you fancy
  end
end

1
投票

嗨,我也遇到了这个错误,我已将脚本更改为 rake 任务,如下所示:

# frozen_string_literal: true

namespace :active_storage do
  desc 'Migrate ActiveStorage files from local to Amazon S3'
  task migrate: :environment do
    module ActiveStorage
      class Downloader
        def initialize(blob, tempdir: nil)
          @blob    = blob
          @tempdir = tempdir
        end

        def download_blob_to_tempfile
          open_tempfile do |file|
            download_blob_to file
            verify_integrity_of file
            yield file
          end
        end

        private

        attr_reader :blob, :tempdir

        def open_tempfile
          file = Tempfile.open(["ActiveStorage-#{blob.id}-", blob.filename.extension_with_delimiter], tempdir)

          begin
            yield file
          ensure
            file.close!
          end
        end

        def download_blob_to(file)
          file.binmode
          blob.download { |chunk| file.write(chunk) }
          file.flush
          file.rewind
        end

        def verify_integrity_of(file)
          raise ActiveStorage::IntegrityError unless Digest::MD5.file(file).base64digest == blob.checksum
        end
      end
    end

    module AsDownloadPatch
      def open(tempdir: nil, &block)
        ActiveStorage::Downloader.new(self, tempdir: tempdir).download_blob_to_tempfile(&block)
      end
    end

    Rails.application.config.to_prepare do
      ActiveStorage::Blob.send(:include, AsDownloadPatch)
    end

    def migrate(from, to)
      configs = Rails.configuration.active_storage.service_configurations
      from_service = ActiveStorage::Service.configure from, configs
      to_service   = ActiveStorage::Service.configure to, configs

      ActiveStorage::Blob.service = from_service

      puts "#{ActiveStorage::Blob.count} Blobs to go..."
      ActiveStorage::Blob.find_each do |blob|
        print '.'
        file = Tempfile.new("file#{Time.now}")
        file.binmode
        file << blob.download
        file.rewind
        checksum = blob.checksum
        to_service.upload(blob.key, file, checksum: checksum)
      rescue Errno::ENOENT
        puts 'Rescued by Errno::ENOENT statement.'
        next
      end
    end

    migrate(:local, :s3)
  end
end


1
投票

一种更简单的方法:

  • amazon
    部分添加到您的
    config/storage.yml

喜欢

  • 将您的存储服务更改为

    :amazon
     中的 
    config/environments/production.rb

  • 将此 rake 任务添加为

    lib/tasks/storage.rake

namespace :storage do
  task reupload: :environment do
    [User, Event].each do |clazz|
      collection = clazz.with_attached_image

      puts "#{clazz} has #{collection.count} images"

      collection.find_each do |user|
        next unless user.image.blob
        user
          .image
          .blob
          .open do |f|
            user.image.attach(io: f, filename: user.image.blob.filename)
          end

        print "."
      end

      puts
    end
  end
end
  • 在本地运行并测试以使用

    rake storage:reupload
    进行尝试(并在
    config.active_storage.service = :amazon
    中更改为
    config/environments/development.rb

  • 检查本地一切是否正常(您的图像应链接到您的 AWS URL)

  • 上传到您的服务器(例如

    cap deploy

  • 在项目目录下运行

    RAILS_ENV=production bundle exec rake storage:reupload

  • 稍等一下,具体取决于您重新上传的图像数量

  • 利润!享受!派对时间!

优点

缺点

  • 没有让它工作
    has_many_attached
    但不应该有太多改变
  • 您必须选择您的型号
  • 它假设它们都被命名为
    image
    (没有什么很难修复的)

0
投票
namespace :active_storage do
  desc 'Migrate ActiveStorage files from local to minio'
  task migrate: :environment do
    module ActiveStorage
      class Downloader
        def initialize(blob, tempdir: nil)
          @blob    = blob
          @tempdir = tempdir
        end

        def download_blob_to_tempfile
          open_tempfile do |file|
            download_blob_to file
            verify_integrity_of file
            yield file
          end
        end

        private

        attr_reader :blob, :tempdir

        def open_tempfile
          file = Tempfile.open(["ActiveStorage-#{blob.id}-", blob.filename.extension_with_delimiter], tempdir)

          begin
            yield file
          ensure
            file.close!
          end
        end

        def download_blob_to(file)
          file.binmode
          blob.download { |chunk| file.write(chunk) }
          file.flush
          file.rewind
        end

        def verify_integrity_of(file)
          raise ActiveStorage::IntegrityError unless Digest::MD5.file(file).base64digest == blob.checksum
        end
      end
    end

    module AsDownloadPatch
      def open(tempdir: nil, &block)
        ActiveStorage::Downloader.new(self, tempdir: tempdir).download_blob_to_tempfile(&block)
      end
    end

    Rails.application.config.to_prepare do
      ActiveStorage::Blob.send(:include, AsDownloadPatch)
    end

    def migrate(from, to)
        config_file = Rails.root.join("config/storage.yml")
      configs = ActiveSupport::ConfigurationFile.parse(config_file)
      from_service = ActiveStorage::Service.configure from, configs
      to_service   = ActiveStorage::Service.configure to, configs

      ActiveStorage::Blob.service = from_service

      puts "#{ActiveStorage::Blob.count} Blobs to go..."
      ActiveStorage::Blob.find_each do |blob|
        print '.'
        file = Tempfile.new("file#{Time.now}")
        file.binmode
        file << blob.download
        file.rewind
        checksum = blob.checksum
        to_service.upload(blob.key, file, checksum: checksum)
      rescue Errno::ENOENT
        puts 'Rescued by Errno::ENOENT statement.'
        next
      end
    end

    migrate(:local, :minio)
  end
end

0
投票

将本地文件迁移到 s3-service 的更简单方法是:

—首先设置为镜像,

— 将所有镜像与 rake 同步,

— 删除镜像设置,

—检查后删除本地文件

我想这个解决方案会有帮助: 如何同步新的 ActiveStorage 镜像?


0
投票

根据 RobbeVP 的回答,我使它可以完成以下任务:

namespace :active_storage do
  task reupload_to_s3: :environment do
    raise "Please switch the active storage service to 'amazon' first" if ENV['ACTIVE_STORAGE_SERVICE'] != 'amazon'
    ActiveStorage::Attachment.find_each do |at|
      next unless at.blob.service_name == "local"
      begin
        blob = at.blob
        blob.open do |f|
          at.record.send(at.name).attach(io: f, content_type: blob.content_type, filename: blob.filename)
        end
      rescue ActiveStorage::FileNotFoundError
        puts "FileNotFoundError: ActiveStorage::Attachment##{at.id}"
      end
    end
  end

  task delete_local_attachments: :environment do
    ActiveStorage::Attachment.find_each do |at|
      next unless at.blob.service_name == "local"
      at.purge
    end
  end
end

0
投票

这是更新且更具描述性的任务

lib/tasks/active_storage_migration.rake

# rails active_storage:migrate

namespace :active_storage do
  desc 'Migrate active storage from one service to another'
  task migrate: :environment do

    module AsDownloadPatch
      def open(tempdir: nil, &block)
        ActiveStorage::Downloader.new(self, tempdir: tempdir).download_blob_to_tempfile(&block)
      end
    end

    Rails.application.config.to_prepare do
      ActiveStorage::Blob.send(:include, AsDownloadPatch)
    end

    def log_info(message)
      puts "[INFO] #{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{message}"
    end

    def log_error(message)
      puts "[ERROR] #{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{message}"
    end

    def migrate(from, to)
      log_info "Started migrating Assets from #{from} to #{to}"

      orphaned_attachments = 0
      missing_file_count = 0
      success_file_count = 0
      config_file = Rails.root.join("config/storage.yml")
      configs = ActiveSupport::ConfigurationFile.parse(config_file)
      from_service = ActiveStorage::Service.configure(from, configs)
      to_service   = ActiveStorage::Service.configure(to, configs)

      ActiveStorage::Blob.service = to_service
      target_attchments = ActiveStorage::Attachment.joins(:blob).where(blob: {service_name: from.to_s})
      log_info "Total #{target_attchments.count} attachments to be processed"

      target_attchments.find_each do |attachment|
        if orphan_attachment?(attachment)
          orphaned_attachments += 1
          log_info "Orphan attachment found: id = #{attachment.id}, resource = #{attachment.record_type}(#{attachment.record_id})"
          next
        end

        begin
          blob = attachment.blob
          if blob.present?
            blob.open do |file|
              attachment.record.send(attachment.name).attach(io: file, content_type: blob.content_type, filename: blob.filename)
            end
            success_file_count += 1
          else
            log_info "Missing blob from attachment = #{attachment.id}"
          end
        rescue ActiveStorage::FileNotFoundError => e
          missing_file_count += 1
          log_error "File Not found for attachment = #{attachment.id}, blob = #{blob.id}"
        rescue => err
          log_error "Failed to upload attachment for record #{attachment.record.class_name}: #{attachment.record.id}"
        end
      end


      log_info "Migration completed! #{success_file_count} Successfully migrated !\n\n"
      log_info "#{orphaned_attachments} orphaned attachments found and skipped. \n\n"
      log_info "#{missing_file_count} Files Missing to upload!"
    end

    def orphan_attachment?(attachment)
      attachment.record_type.safe_constantize.nil? || attachment.record.nil?
    end

    migrate(:local, :amazon)
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.