إعادة إضافة التحميلات المفقودة إلى قاعدة البيانات

I’ve got a site where it seems that a bunch of uploads have been removed from the uploads table in the database, but still exist in the filesystem. This leaves links to these broken. I’ve fixed a few by re-uploading files into a new topic to create the record in the DB (at the time I thought that the files were missing too, but @angus graciously pointed out why I wasn’t finding them–One day I’ll ask: why do we have both sha1 and base62 names for all of these assets?) and then re-baking the posts that include those uploads, like this:

def rebake_posts_with_uploads(topic_id, post_number)
  p = Post.find_by(topic_id: topic_id, post_number: post_number)
  exit unless p
  re = /upload:\/\/(.+?)\)/
  shas= p.raw.scan(re)

  shas.each do |sha|
    posts = Post.where("raw like '%#{sha[0]}%'")
    next unless posts
    puts "Found #{posts.count - 1} #{sha[0]}"
    posts.each do |post|
      next if post.id == p.id
      puts "rebake #{post.id}--#{BASE_URL}/t/-/#{post.topic_id}/#{post.post_number}"
      post.rebake!
    end
  end
end

My new plan, since it seems that the files exist in the file system but not in the database, is to do something like this:

for (all files in /shared/uploads/default/original/1x) do |file|
  unless file is in uploads table
     create upload record 
     for each post that includes that upload record
       rebake

Does that seem right? I’m looking at uploads.rake and don’t see anything that seems to do this already. This is sort of the opposite of

but instead of FileUtils.rm(file_path) I’d instead do an Upload.create, I think.

If this seems really stupid or there is a much better solution, I’d love to hear it before I go down this little rabbit hole.

Thanks.

I don’t know how this happened. I was hoping to pin the blame on a custom plugin, but I’m afraid that’s not the case. It may be related to another discussion, in which someone said:

3 إعجابات

Yeah our history here with uploads is quite spotty, and it is our fault… @sam can you recommend someone to give a quick bit of advice?

إعجابَين (2)

This can work, you just have to be careful to test on local … also look at 2x / 3x directories there are uploads everywhere.

3 إعجابات

I’m trying something like this:

def add_missing_files_to_uploads
  public_directory = Rails.root.join("public").to_s
  db = RailsMultisite::ConnectionManagement.current_db
  uploads_directory = File.join(public_directory, 'uploads', db).to_s
  # uploads and optimized images
  missing = 0
  matched = 0
  Dir.glob("#{uploads_directory}/**/*.pdf").each do |file_path|
    sha1 = Upload.generate_digest(file_path)
    url = file_path.split(public_directory, 2)[1]
    if (Upload.where(sha1: sha1).empty? &&
        Upload.where(url: url).empty?)
      puts "MISSING #{file_path}" if DEBUG
      missing += 1
    else
      matched += 1
    end
  end
  puts "MISS: #{missing}. Match #{matched}"
end

Does that look sort of close? My first test seems to have failed, but I might have screwed something up.

مرحبًا @pfaffman. كنت أقرأ هذا الموضوع لأنه يبدو حلاً واعدًا لـ مشكلتي هنا.
هل تمكنت من الوصول إلى نتيجة جيدة في النهاية؟

عذراً، لا أستطيع التذكر.

يبدو لسام ولي أنها ستعمل.

لقد حللت للتو مشكلة في المحاكي عن طريق إنشاء منشور وإدراج رابط فيه لجميع الصور، ثم إعادة معالجة جميع المنشورات التي تحتوي على transparent.gif.

يبدو هذا حلاً أكثر أناقة إلى حد ما.

أقترح عمل نسخة احتياطية من قاعدة البيانات فقط وتجربتها. أنا في إجازة إلى حد ما، لكن إذا كان لديك ميزانية، يمكنني أن أرى ما يمكنني فعله.

وأنا أيضاً لدي عميل مهم في ams3؛ لم أقوم بنقلهم إلى AWS بعد، لكنني أعتقد أن ذلك سيحدث قريباً. لدي صديق عمل في Digital Ocean وأوصى بـ ams3 لأنها كانت أفضل مركز بيانات لديهم وكانت غير مستغلة إلى حد كبير (كان ذلك منذ وقت طويل). لم تسر الأمور كما كنت أتمنى.

إعجاب واحد (1)

أتساءل ما إذا كانت هذه هي أفضل طريقة في الوقت الحاضر؟

أحاول استعادة الملفات التي تم تحميلها والموجودة على نظام الملفات ولكنها ليست في قاعدة البيانات بعد ترحيل S3 سيئ (باستخدام rake:s3_migrate القديم).

ليس هناك حقًا طريقة جيدة. :crying_cat:

ولكن، نعم. لا تزال هذه هي الفكرة. لا أعتقد أن أي شيء قد تغير في Discourse منذ ذلك الحين. عادةً، سأقوم بإجراء بضعة تعديلات يدويًا للتأكد من أنها تؤدي ما هو متوقع. أيضًا، قم بعمل نسخة احتياطية أولاً، وربما ضع الموقع في وضع القراءة فقط بحيث إذا احتجت إلى استعادة النسخة الاحتياطية فلن تضطر إلى التخلص من أي مشاركات تم إنشاؤها أثناء عبثك بالأشياء.

بدون معرفة الكثير مما يسهل توصيله في منتدى، لا يمكنني أن أقول ما إذا كانت تلك هي أفضل طريقة حقًا أم أن هناك شيئًا أبسط. قد تتمكن من مجرد استخدام gsub لمسارات عناوين URL، على سبيل المثال. إذا كانت لديك ميزانية، يمكنك الاتصال بي أو السؤال في Marketplace.

إعجاب واحد (1)

حسنًا، لقد قمت بحلها باستخدام كلود والكثير من الثناء. أشارك ما فعلته لمساعدة أي شخص آخر لديه مشكلة مماثلة أو متطابقة.

لست متأكدًا مما إذا كانت هذه هي الطريقة الأكثر ذكاءً والأمثل للاستخدام، بل هي الطريقة التي نجحت معي.

يرجى توخي الحذر وتذكر أنني لست خبيرًا بل مبتدئ يتعلم دائمًا.

المشكلة (من S3 إلى نظام الملفات المحلي)

بعد الترحيل من AWS S3 إلى نظام الملفات المحلي، ظهرت العديد من الصور كـ transparent.png. كانت الملفات موجودة دائمًا على القرص ولكن Discourse لم يتمكن من استردادها.

كان السبب الجذري عبارة عن سلسلة مكسورة:

  1. المنشورات ذات عناوين URL القصيرة upload:// (تشفير Base62 لـ SHA1).
  2. قاعدة البيانات uploads تربط SHA1 بمسار ملف محلي.
  3. نظام الملفات يخزن الملفات المسماة بواسطة تجزئة 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 يتخطى عمليات الاستدعاء (callbacks) لذلك تظل ملفات نظام الملفات سليمة.

تم استخدام destroy_all عن طريق الخطأ سابقًا وأدى إلى تشغيل عمليات الاستدعاء التي نقلت الملفات إلى سجل الحذف المؤقت.

استعدت ملفًا فرديًا استخدمته للاختبار وأعدت صياغة طريقتي.

3 إعجابات