views:

218

answers:

1

I have a rails application that is using attachment_fu. Currently, it is using :file_system for storage, but I want to change it to :s3, to allow for better scaling as more files get uploaded.

What is involved with this? I imagine that if I just switch the code to use :s3, all the old links will be broken. Do I need to just copy the existing files from the file system to S3? A google search hasn't turned up much on the topic.

I would prefer to move the existing files over to S3, so everything is in the same place, but if necessary, the old files can stay where they are, as long as new ones go to S3.

EDIT: So, it is not as simple as copying over the files to S3; the URLs are created using a different scheme. When they are stored in :file_system, the files end up in places like /public/photos/0000/0001/file.name, but the same file in :s3 might end up in 0/1/file.name. I think it is using the id something, and just padding it (or not) with zeros, but I'm not sure of that.

+1  A: 

That's correct. The ids are padded using :file_system storage. Instead of renaming all your files, you can alter the s3 backend module to use padded numbers as well.

Copy the partitioned_path method from file_system_backend.rb and put it in s3_backend.rb.

    def partitioned_path(*args)
      if respond_to?(:attachment_options) && attachment_options[:partition] == false
        args
      elsif attachment_options[:uuid_primary_key]
        # Primary key is a 128-bit UUID in hex format. Split it into 2 components.
        path_id = attachment_path_id.to_s
        component1 = path_id[0..15] || "-"
        component2 = path_id[16..-1] || "-"
        [component1, component2] + args
      else
        path_id = attachment_path_id
        if path_id.is_a?(Integer)
          # Primary key is an integer. Split it after padding it with 0.
          ("%08d" % path_id).scan(/..../) + args
        else
          # Primary key is a String. Hash it, then split it into 4 components.
          hash = Digest::SHA512.hexdigest(path_id.to_s)
          [hash[0..31], hash[32..63], hash[64..95], hash[96..127]] + args
        end
      end
    end

Modify s3_backend.rb's full_filename method to use the partitioned_path.

    def full_filename(thumbnail = nil)
      File.join(base_path, *partitioned_path(thumbnail_name_for(thumbnail)))
    end

attachment_fu will now create paths with the same names as it did with the file_system backend, so you can just copy your files over to s3 without renaming everything.

nilbus