Как извлекать образы из бакета, который вам не принадлежит

У меня есть сайт с множеством изображений в бакете CDCK. У меня осталось немного времени, чтобы загрузить их из этого бакета и переместить на локальный сервер (а затем — в другой бакет).

Задачи uploads:analyze_missing_s3 и uploads:fix_missing_s3 не находят никаких проблем, хотя примерно 25 тысяч изображений находятся в удалённом бакете.

Предложение по улучшению функционала: Похоже, что EnsureS3UploadsExistence должен помечать загрузки как invalid_etag, если они находятся не в правильном бакете (то есть у них нет прав доступа к этому бакету?), но этого не происходит. Мне кажется логичным, чтобы эта или какая-либо другая задача определяла, находятся ли загрузки в бакете, которым текущий сайт не управляет, или просто не в ожидаемом для сайта бакете.

Я не совсем понимаю, что означает verification_status, так как он, похоже, не проверяет, что всё в порядке.

Что я пробовал

ups=Upload.where("url like '//discourse-cloud-file-uploads.s3.dualstack.us-west-2.amazonaws.com/business6%'")
ups.update_all(verification_status: 3)

а затем

rake uploads:fix_missing_s3

Эта задача Rake выводила сообщения о загрузке изображений, но через день, когда посты были пересобраны, эти изображения в raw выглядели как ![zawfI7C|346x500](upload://9L0PqY4QpqLOfexXHMMbv00EgaB.jpeg), но ссылались на: https://test.literatecomputing.com/images/transparent.png

Возможно, стоит написать скрипт, который:

  1. Список файлов
  2. wget этот список
  3. aws s3 sync их в ваш бакет
  4. remap oldbucket newbucket

Ну, черт. Я боялся, что ответ будет именно таким.

И, думаю, @RGJ тоже поступает так же. :crying_cat_face:

Но я не могу получить список файлов, так как они находятся в вашем бакете, и я почти уверен, что для этого нужны учетные данные.

rake uploads:fix_missing_s3, похоже, загрузил (большинство?) файлы в локальную файловую систему (загрузки для этого сайта еще не находятся в S3).

Поэтому я сделал следующее, чтобы исправить загрузки:

def fix_bad_uploads(bad_uploads)
  fixed = 0
  retrieved = 0
  missing = 0
  bad_bucket="//discourse-cloud-file-uploads.s3.dualstack.us-west-2.amazonaws.com/business6/uploads/forumosa"
  bad_uploads.each do |upload|
    url = URI.parse("https:"+upload.url)
    upload.url=upload.url.gsub(bad_bucket,"/uploads/default")
    if File.exists?("/shared/#{upload.url}")
      fixed += 1
      print "1"
      upload.save
    # posts = Post.where("raw like '%#{upload.short_url}%'")
    # posts.each do |post|
    #   post.rebake!
    #   print "."
    # end
    else
      begin
        # retrieve missing
        filename = "/shared#{upload.url}"
        dirname = File.dirname(filename)
        unless File.directory?(dirname)
          FileUtils.mkdir_p(dirname)
        end
        file = File.new(filename, "w")
        Net::HTTP.start(url.host) do |http|
          resp = http.get(url.path)
          open(file, "wb") do |file|
            file.write(resp.body)
          end
        end
        file.close
        print "+"
        upload.save if File.exists?(filename)
      rescue => e
        puts "bad: #{e}"
        missing += 0
        sleep 1
        print "0"
      end
    end
  end
end

Это исправило большинство из них. Но, похоже, есть некоторые посты, которые содержат запись uploads://, для которой в базе данных нет записи Upload. Пересборка таких постов приводит к появлению transparent.png.

Поэтому я попробовал что-то вроде этого:

def get_missing_short_url(short_url)
  prefix = "https://discourse-cloud-file-uploads.s3.dualstack.us-west-2.amazonaws.com/business6/uploads/forumosa/original/3X"
  remove_url = "https://discourse-cloud-file-uploads.s3.dualstack.us-west-2.amazonaws.com/business6/uploads/forumosa/"
  sha1= Upload.sha1_from_short_url(short_url)
  extension = short_url.split(".").last
  upload = Upload.find_by(sha1: sha1)
  if !upload
    # try to find it in s3
    one = sha1[0]
    two=sha1[1]
    url_link = "#{prefix}/#{one}/#{two}/#{sha1}.#{extension}"
    puts "URL: #{url_link}"
    sleep 1
    url = URI.parse(url_link)
    full_filename = url_link.gsub(remove_url,"/shared/uploads/default/")
    filename = "/tmp/#{File.basename(url_link.gsub(remove_url,"/shared/uploads/default/"))}"
    dirname = File.dirname(filename)
    unless File.directory?(dirname)
      FileUtils.mkdir_p(dirname)
    end
    File.open(filename, "w") do |file|
      Net::HTTP.start(url.host) do |http|
        resp = http.get(url.path)
        open(file, "wb") do |file|
          file.write(resp.body)
        end
      end
    end
      # make upload for file
    File.open(filename, "r") do |file|
      upload = UploadCreator.new(
        file,
        File.basename(file),
      ).create_for(Discourse.system_user.id)
    end
    if upload.persisted?
      puts "We did it! #{upload.id}"
    else
      puts "darn. #{upload.errors.full_messages}"
      sleep 5
    end
    File.open(filename, "w") do |file|
      Net::HTTP.start(url.host) do |http|
        resp = http.get(url.path)
        open(file, "wb") do |file|
          file.write(resp.body)
        end
      end
    end
    end
  upload
end

Это в основном работает, но в моих тестах иногда мне не удается корректно определить правильный URL S3 из sha, который я получаю из short_url. Не уверен, как это исправить.

Кроме того, один из файлов каким-то образом оказался с sha, отличным от того, что указан в имени файла пути S3.

Моя текущая идея заключается в том, чтобы начать с перебора всех записей cooked, извлечь все URL-адреса https://discourse-cloud-file-uploads, а затем обновить записи Upload, которые на них ссылаются, и создать отсутствующие.

Не упускаю ли я что-то очевидное?

Разве таблица uploads не является списком файлов?

Я так и думал! Но в поле raw есть ссылки вида uploads://, для которых нет записей в таблице uploads (по крайней мере, когда я ищу по sha, полученному через Upload.sha1_from_short_url(short_url)).

В большинстве случаев, но не во всех, мне удалось вывести URL хранилища по sha (я не до конца понимаю 1X, 2X, 3X, но кажется, что они все находятся в 3x).

Так что нет, таблица uploads не является полным списком соответствующих файлов.