I want to clone an attached image to another model. Either as:
a reference, such that the attachment won't be deleted until all the referring objects are deleted
by cloning the attachment and having it as a distinct replica...
How does rails handle ActiveStorage attachments? Do they get deleted automatically when the model goes or does it need to be done manually? Is there a reference counting mechanism?
This worked for me in Rails 6.1.3
model
.new_file
.attach(io: StringIO.new(original_file.download),
filename: original_file.filename,
content_type: original_file.content_type)
Related
Background:
I'm developing Rails Application and in some models I'm using paperclip gem to save attachments. In same models I'm using public_activity gem to track the model changes & I've prepared Restore functionality based on that to be able to Undo changes on that model with specific conditions.
Now using paperclip option :preserve_files => true it's simple to get the old file in case of delete, but in case of update, I don't know how.
Question:
public_activity gem is already using before_action callback and I can handle all dirty fields except Paperclip file update.
So how can I get the dirty updated file without changing my models or adding extra callbacks (because I'm using this achievement in multiple models and Undo functionality is generic).
Note:
I'm using File.exist?(paperclip_attachment.path) to check whether the file is still exist or no, and it returns false in case of update callback of public_activity (I think it is same as before_update callback).
I've did a simple way to get the dirty file with just edit Undo Functionality in public_activity update callback:
dirty_file_path = Dir.glob(File.join(File.dirname(paper_clip_attachment.path), '*.*')).max { |a,b| File.ctime(a) <=> File.ctime(b) }
paper_clip_attachment.path includes the path to the file, but the file itself is not yet created.
The code above just checks the container folder of the file paper_clip_attachment.path then checks the latest created file and save it's path to be used in Undo step.
I would like to migrate from Paperclip to Carrier Wave or Refile because of this. The solution written here is impressive, but strikes me as complex and perhaps brittle.
My Rails4 app has 100's of images in production that were uploaded with Paperclip. Files are stored on production server. I have looked for a complete set of steps to follow to migrate, but keep coming up empty.
Is there a set of steps one can follow that allows for migration without necessitating application code re-write?
Alternatively, is there another way to persist uploaded files in Paperclip when form validation fails?
What am I missing here?
UPDATE:
Tried the solution detailed here by https://stackoverflow.com/users/646389/galatians . My paperclip :path and :url interpolations make use of :id_partition. I don't see a way this can be reconciled with an uploaded Image that is staged, but not yet saved.
I migrated to Carrierwave. Here are the relevant stats:
Time to work on and fail at coding a solution for persistent files
across form reloading with Paperclip - 4 hours. See OP update for the issue I could not overcome.
Time to Migrate to carrierwave, adjust the relevant models, controllers, and forms, and test. - 2 hours. Not so bad.
This key info helped me adjust the paths correctly. Keeping the path identical was important to me for avoiding having to move images to a new location in production:
Carrierwave code for generating paperclip-like :path and :url info here.
Paperclip interpolation info here.
This link got me on the right track, although my default :path made use of :id_partition not :id.
UPDATE:
Migration breaks this paradigm:
#protocol.images.each do |i|
tmp=i.dup
tmp.avatar = File.open(i.avatar.current_path)
tmp.save!
#dest.images << tmp
end
See: Duplicating a record that contains a carrierwave avatar : Getting "can't convert nil into Integer" error
You don't need to do anything because the only data needed to get images is already stored in DB.
I'm uploading multiple files with Carrierwave gem as pointed out here: https://github.com/carrierwaveuploader/carrierwave#multiple-file-uploads
I can delete them all in one shot with no problem with user.remove_photos! being photos an array or json in the db; however, how do I delete selectively one or two out of the complete set of files previously uploaded?
#user.remove_photos! <--- removes all the photos
#user.save
#user.remove_photos[0]! <--- does not work
By the way, I'm using sqlite3 and in order to make it work you need to do the following tweaks:
rails g migration add_photos_to_users photos:string <-- note is string
and in your model definition, after mounting the uploader:
serialize :photos, JSON
both, not yet shown on the Readme, although the commit was already done by rusikf here: https://github.com/rusikf/carrierwave/commit/dedc9c341e34f2e0e57d47c3eec65347fef398bd
I use paperclip for the attachments on my models. I've recently added a fingerprint to the attachments, it works fine with the new attachments but it ws not added to the old ones, because of this I'm having trouble reprocessing attachments to add a new style, because paperclip tries to find the attachments with fingerprint and not all the attachments have one.
Is there a way to add the fingerprint to the old attachments?
This is the error I'm getting from the reprocessing on the old attachments:
Errno::ENOENT: No such file or directory - /tmp/paperclip-reprocess20140422-1036-nl9gxk
[edit]
Have you tried?
$ rake paperclip:refresh class=Model_Name_Goes_Here
[/edit]
if you don't have too many images. simplest way, I can think of is:
Dir.foreach('/path/to/dir/that/contains/old/images') do |item|
next if item == '.' or item == '..'
file = File.join(File.expand_path(File.dirname(__FILE__)), item)
thing.image = file
thing.save
end
you can run this script, using runner:
$ rails runner /path/to/script
Does something like this work for you?
After manually try and examine the error several times I found that the problem is not that the attachment had no fingerprint. The problem was that the fingerprint was nil. For some reason paperclip does not handle this case.
I manually gave every object an empty string as a fingerprint through the database (this saved me from renaming the file on disk) and then reprocess every one of theme.
That solved it!
It looks like Paperclip generates a fingerprint for an attachment only when the original style is changed. I want to keep the original style unchanged but crop one of my other styles (let's call it "listing"). Is there a way to force Paperclip to regenerate a fingerprint if the "listing" style is cropped but the "original" style is not?
I found generate_fingerprint no longer works with the latest Paperclip (4.1.1) but the refresh rake task does: e.g. rake paperclip:refresh CLASS=Dog.
In my case, I wanted the fingerprint to be created automatically when I ran the the migration that added the fingerprint, so I dug into the code and found you can call reprocess! on the attachment and that'll also do the trick.
I recently added an image_fingerprint column to an existing model that has Paperclip images attached.
I forced Paperclip to generate fingerprints for the existing images with the generate_fingerprint method on Paperclip::Attachment:
class Dog
has_attached_file :image
...
end
Dog.all.each do |dog|
dog.image_fingerprint = dog.image.generate_fingerprint(dog.image)
...
end