好的,我通过 Claude 和大量的赞美解决了这个问题。我将分享我的做法,希望能帮助到遇到类似或相同问题的其他人。
我不确定这是不是最巧妙和最佳的使用方法,但它对我有效。
请小心并记住,我不是专家,而是一个不断学习的新手。
问题所在 (S3 → 本地文件系统)
从 AWS S3 迁移到本地文件系统后,很多图片显示为 transparent.png。文件总是在磁盘上,但 Discourse 无法解析它们。
根本原因是链条断裂:
- 带有
upload://短链接(base62 编码的 SHA1)的帖子。 - 数据库中将 SHA1 映射到本地文件路径的
uploads记录。 - 以其 SHA1 哈希命名的文件文件系统。
迁移正确地将文件移动到了磁盘,但没有存在的 uploads 数据库记录。如果没有匹配的记录,Discourse 会回退到 transparent.png。
解决方案 (创建记录并重新烘焙)
从孤立文件中创建缺失的上传记录:
dir = Rails.root.join("public", "uploads", "default", "original")
created = 0
Dir.glob(dir.join("**", "*")).select { |f| File.file?(f) }.each do |path|
sha = File.basename(path, File.extname(path))
next if Upload.find_by(sha1: sha)
ext = File.extname(path).delete(".")
relative = path.sub("#{Rails.root}/public", "")
u = Upload.new
u.sha1 = sha
u.url = relative
u.original_filename = File.basename(path)
u.filesize = File.size(path)
u.extension = ext
u.user_id = -1
u.save!(validate: false)
created += 1
puts "Created upload #{u.id}: #{sha}"
end
puts "Total created: #{created}"
重新烘焙引用了已恢复上传的帖子:
fixed_posts = 0
Upload.where(user_id: -1).find_each do |u|
short = u.short_url
next unless short
Post.where("raw LIKE '%upload://%'").find_each do |p|
urls = p.raw.scan(/upload:\/\/^[^\s\]\)]+/)
urls.each do |url|
decoded = Upload.sha1_from_short_url(url)
if decoded == u.sha1
p.rebake!
fixed_posts += 1
puts "Rebaked post #{p.id}"
break
end
end
end
end
puts "Total rebaked: #{fixed_posts}"
重新生成缺失的优化文件:
修复原始文件后,我们需要填充优化文件(1X、2X 等)。
rake uploads:regenerate_missing_optimized
安全回滚(以防万一)
所有创建的记录都使用 user_id: -1。要撤销:
Upload.where(user_id: -1).delete_all
delete_all 会跳过回调,因此文件系统中的文件不会被触动。
之前错误地使用了
destroy_all,它触发了将文件移至墓碑的回调。我恢复了用于测试的单个文件,并重构了我的方法。