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

6
6个回答

5

根据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
谢谢RobbeVP,它对我有用!只是在使用blob.destroy时出现了异常ActiveRecord::InvalidForeignKey。我通过添加另一个rake任务来删除旧的附件进行修复:ActiveStorage::Attachment.find_each {|at| at.purge if at.blob.service_name == "local"} - Zernel

1
一个更简单的方法是:
  • 在你的config/storage.yml中添加一个amazon部分

像这样:

  • 在你的config/environments/production.rb中将存储服务更改为:amazon

  • 将此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/environments/development.rb中更改为config.active_storage.service = :amazon

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

  • 上传到服务器(例如cap deploy

  • 在项目目录中运行RAILS_ENV=production bundle exec rake storage:reupload

  • 等待一会儿,具体取决于要重新上传多少个图像

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

优点

缺点

  • 无法使has_many_attached正常工作,但不应该有太多更改
  • 您必须选择您的模型
  • 它假设所有内容都命名为image(没有太难修复的问题)

一个快速的解决方法是使用 send(:open) 而不是 open - Dorian
任务文件名应为:lib/tasks/storage.rake - MIA

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


我稍后会检查它。 - Vishal

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

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

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

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

— 首先设置为镜像,

— 使用rake同步所有镜像,

— 检查后删除镜像设置,

— 删除本地文件。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接