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

لدي موقع يبدو أن مجموعة من الملفات المرفوعة قد حُذفت من جدول uploads في قاعدة البيانات، لكنها لا تزال موجودة في نظام الملفات. هذا يترك الروابط指向 هذه الملفات مكسورة. قمت بإصلاح بعضها عن طريق إعادة رفع الملفات إلى موضوع جديد لإنشاء السجل في قاعدة البيانات (في ذلك الوقت اعتقدت أن الملفات مفقودة أيضًا، لكن @angus أشار بلطف إلى السبب الذي لم أتمكن فيه من العثور عليها—في يوم ما سأسأل: لماذا نملك كلا من أسماء sha1 و base62 لكل هذه الأصول؟) ثم قمت بإعادة معالجة المنشورات التي تتضمن هذه الملفات المرفوعة، مثل هذا:

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

خطتي الجديدة، بما أن الملفات موجودة في نظام الملفات ولكنها غير موجودة في قاعدة البيانات، هي القيام بشيء مثل هذا:

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

هل يبدو ذلك صحيحًا؟ أنا أراجع uploads.rake ولا أرى أي شيء يبدو أنه يفعل ذلك بالفعل. هذا إلى حد ما عكس

لكن بدلاً من FileUtils.rm(file_path) سأقوم بـ Upload.create، أعتقد.

إذا بدا هذا غبيًا حقًا أو إذا كان هناك حل أفضل بكثير، فسأحب سماعه قبل أن أغوص في هذا النفق الصغير.

شكرًا.

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

نعم، تاريخنا هنا مع التحميلات غير مستقر، والخطأ منا.. @سام، هل يمكنك التوصية بشخص لتقديم نصيحة سريعة؟

يمكن أن ينجح هذا الأمر، لكن عليك فقط الحذر من إجراء الاختبار على البيئة المحلية … كما انظر إلى مجلدات 2x / 3x، فهناك عمليات تحميل في كل مكان.

أحاول شيئًا مثل هذا:

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
  # ملفات التحميل والصور المحسّنة
  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

هل يبدو ذلك قريبًا إلى حد ما؟ يبدو أن اختباري الأول قد فشل، لكن قد يكون لديّ خطأ ما في شيء ما.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

المشكلة (من 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 عن طريق الخطأ سابقًا وأدى إلى تشغيل عمليات الاستدعاء التي نقلت الملفات إلى سجل الحذف المؤقت.

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