Class: S3MetadataCorrectionWorker

Inherits:
Object
  • Object
show all
Includes:
Sidekiq::Worker
Defined in:
app/workers/s3_metadata_correction_worker.rb

Overview

Worker to correct S3 object metadata for misnamed files
Called when we detect a mismatch between file content and stored metadata
(e.g., a PNG file stored with JPEG mime type)

This updates both the S3 Content-Type header AND Dragonfly's custom
x-amz-meta headers so future fetches have correct metadata.

Instance Method Summary collapse

Instance Method Details

#perform(app_name, uid, correct_mime, correct_ext = nil) ⇒ Object

Parameters:

  • app_name (String)

    Dragonfly app name ('secure', 'media')

  • uid (String)

    The S3 object UID/key

  • correct_mime (String)

    The correct MIME type (e.g., 'image/png')

  • correct_ext (String, nil) (defaults to: nil)

    The correct file extension (e.g., 'png')



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/workers/s3_metadata_correction_worker.rb', line 18

def perform(app_name, uid, correct_mime, correct_ext = nil)
  return unless app_name.present? && uid.present? && correct_mime.present?

  app = Dragonfly.app(app_name.to_sym)
  return unless app

  datastore = app.datastore
  return unless datastore.respond_to?(:storage)

  storage = datastore.storage
  bucket_name = datastore.bucket_name
  root_path = datastore.root_path
  full_key = root_path.present? ? "#{root_path}/#{uid}" : uid

  # Build headers for S3 copy (metadata update pattern)
  # Include both Content-Type AND Dragonfly's x-amz-meta headers
  headers = {
    'Content-Type' => correct_mime,
    'x-amz-metadata-directive' => 'REPLACE',
    'x-amz-acl' => 'public-read',
    # Dragonfly stores these as custom metadata
    'x-amz-meta-mime-type' => correct_mime
  }

  # Include name/extension if provided
  if correct_ext.present?
    headers['x-amz-meta-name'] = "file.#{correct_ext}"
  end

  # Copy object to itself with new metadata
  storage.copy_object(
    bucket_name, full_key,
    bucket_name, full_key,
    headers
  )

  Rails.logger.info do
    "[S3MetadataCorrection] Corrected metadata for #{uid}: Content-Type -> #{correct_mime}"
  end
rescue Fog::Errors::NotFound
  # File was deleted, nothing to fix
  Rails.logger.debug { "[S3MetadataCorrection] Object not found: #{uid}" }
rescue StandardError => e
  Rails.logger.warn { "[S3MetadataCorrection] Failed for #{uid}: #{e.message}" }
  raise # Re-raise for Sidekiq retry
end