How can I reorganize an existing folder hierarchy

2020-06-03 06:46发布

问题:

I am trying to move files around my S3 bucket using CarrierWave to reorganize the folder structure.

I came to an existing Rails application where all images for a class are being uploaded into a folder called /uploads. This is causing problems where if two users upload different images with the same file-name, the second image overwrites the first. To solve this, I want to reorganize the folders to place each image in its own directory according to the ActiveRecord object instance. We are using CarrierWave to manage file uploads.

The old uploader code had the following method:

def store_dir
  "uploads"
end

I modified the method to reflect my new file storage scheme:

def store_dir
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end

This works great for new images, but breaks the url for old images. Existing images report their URL to be inside the new folder immediately when I change the model, while the image files are still stored in /uploads.

> object.logo.store_dir
=> "uploads/object/logo/133"

This is not correct. This object should report its logo in /uploads.

My solution is to write a script to move the image files, but I haven't found the correct methods in CarrierWave to move the files. My script would look something like this:

MyClass.all.each |image|
  filename = file.name #This method exists in my uploader, returns the file name
  #Move the file from "/uploads" to "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end 

What should I do in line three of my script to move the file to a new location?

回答1:

WARNING: This is untested, so please don't use on production before testing it out.

Here's the thing, once you change the contents of 'store_dir', all your old uploads will become missing. You know this already. Interacting with S3 directly seems like the most obvious way of solving this, since carrierwave doesn't have a move function.

One thing that might work, would be to re-'store' your uploads and change the 'store_dir' path in the 'before :store' callback.

In your uploader:

#Use the old uploads directory so carriewave knows where the original upload is
def store_dir
  'uploads'
end

before :store, :swap_out_store_dir

def swap_out_store_dir
  self.class_eval do
    def store_dir
      "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    end
  end
end

And then run a script like this:

MyClass.all.each do |image|
  image.image.cache! #create a local cache so that store! has something to store
  image.image.store!
end

After this, verify that the files have been copied to the correct locations. You'll then have to delete the old upload files. Also, remove the one time use uploader code above and replace it with your new store_dir path:

def store_dir
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id} "
end

I haven't tested this out, so I can't guarantee it will work. Please use test data first to see if it works and comment here if you've had any success.