title: Need to change the storage "directory" of files in an S3 Bucket (Carrierwave / Fog)
body: <p>I am using Carrierwave with 3 separate models to upload photos to S3.  I kept the default settings for the uploader, which was to store photos in a root S3 bucket.  I then decided to store them in sub-directories according to model name like /avatars, items/, etc. based on the model they were uploaded from...</p>
  
  <p>Then, I noticed that files of the same name were being overwritten and when I deleted a model record, the photo wasn't being deleted.  </p>
  
  <p>I've since changed the store_dir from an uploader-specific setup like this:</p>
  
  <pre><code>  def store_dir
      "items"
    end
  </code></pre>
  
  <p>to a generic one which stores photo under the model ID (I use mongo FYI):</p>
  
  <pre><code>  def store_dir
      "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    end
  </code></pre>
  
  <p>Here comes the problem.  I am trying to move all the photos already into S3 into the proper "directory" within S3.  From what I've ready, S3 doesn't have directories per se.  I'm having trouble with the rake task.  Since i changed the store_dir, Carrierwave is looking for all the photos previously uploaded in the wrong directory.</p>
  
  <pre><code>namespace :pics do
    desc "Fix directory location of pictures on s3"
    task :item_update =&gt; :environment do
      connection = Fog::Storage.new({
        :provider                 =&gt; 'AWS',
        :aws_access_key_id =&gt; 'XXXX',
        :aws_secret_access_key =&gt; 'XXX'
      })
      directory = connection.directories.get("myapp-uploads-dev")
  
      Recipe.all.each do |l|
        if l.images.count &gt; 0
          l.items.each do |i|
            if i.picture.path.to_s != ""
              new_full_path = i.picture.path.to_s
              filename = new_full_path.split('/')[-1].split('?')[0]
              thumb_filename = "thumb_#{filename}"
              original_file_path = "items/#{filename}"
              puts "attempting to retrieve: #{original_file_path}"
              original_thumb_file_path = "items/#{thumb_filename}"
              photo = directory.files.get(original_file_path) rescue nil
              if photo
                puts "we found: #{original_file_path}"
                photo.expires = 2.years.from_now.httpdate
                photo.key = new_full_path
                photo.save
                thumb_photo = directory.files.get(original_thumb_file_path) rescue nil
                if thumb_photo
                  puts "we found: #{original_thumb_file_path}"
                  thumb_photo.expires = 2.years.from_now.httpdate
                  thumb_photo.key = "/uploads/item/picture/#{i.id}/#{thumb_filename}"
                  thumb_photo.save
                end
              end
            end
          end
        end
      end
    end
  end
  </code></pre>
  
  <p>So I'm looping through all the Recipes, looking for items with photos, determining the old Carrierwave path, trying to update it with the new one based on the store_dir change.  I thought if I simply updated the photo.key with the new path, it would work, but it's not.</p>
  
  <p>What am I doing wrong?  Is there a better way to accomplish the ask here?</p>
  
  <p><strong>Here's what I did to get this working...</strong></p>
  
  <pre><code>namespace :pics do
    desc "Fix directory location of pictures"
    task :item_update =&gt; :environment do
      connection = Fog::Storage.new({
        :provider                 =&gt; 'AWS',
        :aws_access_key_id =&gt; 'XXX',
        :aws_secret_access_key =&gt; 'XXX'
      })
      bucket = "myapp-uploads-dev"
      puts "Using bucket: #{bucket}"
      Recipe.all.each do |l|
        if l.images.count &gt; 0
          l.items.each do |i|
            if i.picture.path.to_s != ""
              new_full_path = i.picture.path.to_s
              filename = new_full_path.split('/')[-1].split('?')[0]
              thumb_filename = "thumb_#{filename}"
              original_file_path = "items/#{filename}"
              original_thumb_file_path = "items/#{thumb_filename}"
              puts "attempting to retrieve: #{original_file_path}"
              # copy original item
              begin
                connection.copy_object(bucket, original_file_path, bucket, new_full_path, 'x-amz-acl' =&gt; 'public-read')
                puts "we just copied: #{original_file_path}"
              rescue
                puts "couldn't find: #{original_file_path}"
              end
              # copy thumb
              begin
                connection.copy_object(bucket, original_thumb_file_path, bucket, "uploads/item/picture/#{i.id}/#{thumb_filename}", 'x-amz-acl' =&gt; 'public-read')
                puts "we just copied: #{original_thumb_file_path}"
              rescue
                puts "couldn't find thumb: #{original_thumb_file_path}"
              end
  
            end
          end
        end
      end
    end
  end
  </code></pre>
  
  <p>Perhaps not the prettiest thing in the world, but it worked.</p>