I'm having a hard time figuring out how to prevent Paperclip from deleting the old version of an attachment (image).
I have a model, Site, which has an attachment, logo. I would like to keep the old logos around since I will be keeping track of changes to the model and would like to view the history of logos.
I'm keeping track of the changes in another model, which has a reference to file paths. My problem is that when updating a site with a new logo, Paperclip will flush the old logo first.
It surprises me that there's not an option you can switch to prevent Paperclip from flushing the old attachment before creating the new one.
Any ideas?
There's a new option that tells paperclip to preserve old attachments:
https://github.com/thoughtbot/paperclip/commit/65e8d4f6de50732d8e1b
https://github.com/thoughtbot/paperclip/issues/60
Simple to use:
has_attached_file => :attachment,
:styles => { :thumb => 100x100! },
:preserve_files => true
It's not documented yet and took some digging to find so I wanted to share it here.
Because attachments are defined at the class level, Paperclip interpolates the symbols in your strings using it's own interpolation library. You can create your own interpolations using this library.
I would add a field to the model called attachment_version or something similar, and then increment this version number each time the file is changed. Then, create an interpolation for it in an initializer file:
Paperclip.interpolates :version do |attachment, style|
attachment.instance.attachment_version
end
Now you can use :version in your strings:
class Model < ActiveRecord::Base
has_attached_file :something, :path => " :rails_root/public/somethings/etc/:version.:extension"
end
See the wiki documentation for more information.
[Update]
After some digging around (see the comments to this answer), I've come to the conclusion that Paperclip will still delete the old attachment due to code that's called in Paperclip::Atachment#attach. Probably the best way to deal with this is to create a new storage engine based on Paperclip::Storage::Filesystem and overwrite #flush_deletes. Note that there is no way in that method to tell if a file is being queued for deletion because of the model it belongs to being deleted or a new file is being uploaded in its place.
lib/paperclip_monkey_patch.rb:
module Paperclip
class Attachment
def clear
# nop
#raise "hell"
# op
instance_write(:file_name, nil)
instance_write(:content_type, nil)
instance_write(:file_size, nil)
instance_write(:updated_at, nil)
end
end
end
Then add this line at the top of any file that deleted attachments:
require 'paperclip_monkey_patch'
Thanks to Ruby Forum
I had a similar issue with Paperclip attachments at when working on a Rails blog last summer.
There is a patch that addresses this. I wasn't able to get it working for myself, but it's worth a shot!
http://github.com/alainravet/paperclip/tree/keep_old_files
Related
I'm using Paperclip-FFMEG to upload video files to my development environment (and, eventually, to a local server when my project goes into production).
When videos are uploaded, the filename is, by default, as follows:
/system/modelnames/paperclipnames/.../mynewfile.mp4?xxxxxxxxxx
I believe the 10 digit figure after the questionmark is a timestamp.
However, the player I will be using to play the videos doesn't like to have anything after the file attachment - so I would like to strip the questionmark, and the timestamp after it, before passing the URL into the player.
I tried to use the following Ruby (I think) strip function:
temp_variable = model.paperclipattribute.url(:blah).strip('?')[0]
However, Rails throws up an error:
wrong number of arguments(1 for 0)
I take it I'm doing this wrong? Any other solutions? I don't want to switch off timestamps entirely, as I only need to do so in this situation.
Thanks!
If you want to do this everywhere for a given attachment and without the need to pass the extra parameter, you can set the use_timestamp option when calling the has_attached_file method in your model. So, to build on the example given in the Paperclip README:
has_attached_file :avatar,
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:default_url => "/images/:style/missing.png",
:use_timestamp => false
Hope this is OK to put as an answer to my own question (as it may be useful for others who stumble across this post), but I've since discovered that an alternative (and more appropriate) way to deal with this issue is to add the false parameter to URL() as follows when displaying the content in your view:
model.paperclipattribute.url(:whateverstyle, false)
The timestamp will automatically be removed. I think this is better, as the split method I suggested might remove content you don't intend to remove - for example, if your file is called something like "Is_this_a_question_?_Yes_it_is.mp4?xxxxxx", then everything after the first question mark might be removed (i.e. the file will be read as "Is this a question_", thus corrupting the filename.
I haven't tested this, so I may be wrong.
Globally default them to off, just put this in a config/initializers/paperclip.rb file.
Paperclip::Attachment.default_options[:use_timestamp] = false
You want to use split instead I believe.
strip doesn't take any argument, it just removes leading and trailing whitespaces
I'm using Paperclip / S3 for file uploading. I upload text-like files (not .txt, but they are essentially a .txt). In a show controller, I want to be able to get the contents of the uploaded file, but don't see contents as one of its attributes. What can I do here?
attachment_file_name: "test.md", attachment_content_type: "application/octet-stream", attachment_file_size: 58, attachment_updated_at: "2011-06-22 01:01:40"
PS - Seems like all the Paperclip tutorials are about images, not text files.
In Paperclip 3.0.1 you could just use the io_adapter which doesn't require writing an extra file to (and removing from) the local file system.
Paperclip.io_adapters.for(attachment.file).read
#jon-m answer needs to be updated to reflect the latest changes to paperclip, in order for this to work needs to change to something like:
class Document
has_attached_file :revision
def revision_contents(path = 'tmp/tmp.any')
revision.copy_to_local_file :original, path
File.open(path).read
end
end
A bit convoluted as #jwadsack mentioned using Paperclip.io_adapters.for method accomplishes the same and seems like a better, cleaner way to do this IMHO.
To access the file you can use the path method:
csv_file.path
http://rdoc.info/gems/paperclip/Paperclip/Attachment#path-instance_method
This can be used along with for example the CSV reader.
Here's how I access the raw contents of my attachment:
class Document
has_attached_file :revision
def revision_contents
revision.copy_to_local_file.read
end
end
Please note, I've omitted my paperclip configuration options and any sort of error handling.
You would need to load the contents of the file (using Rubys File.open) into a variable before you show it. This may be an expensive operation if your app gets lots of use, so it may be worthwhile reading the contents of the file and putting it into a text column in your database after uploading it.
Attachment already inherits from IOStream. http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment
So it should just be "#{attachment}" or <% RDiscount.new(attachment).to_html %> or send_data(attachment). However you wanted to display the data.
This is a method I used for upload from paperclip to active storage and should provide some guidance on temporarily working with a file in memory. Note: This should only be used for relatively small files.
Written for gem paperclip 6.1.0
Where I have a simple model
class Post
has_attached_file :image
end
Working with a temp file in ruby so we do not have to worry about closing the file
Tempfile.create do |tmp_file|
post.image.copy_to_local_file(nil, tmp_file.path)
post.image_temp.attach(
io: tmp_file,
filename: post.image_file_name,
content_type: post.image_content_type
)
end
I've had a system thats been running fine for ages using Rails 3 & Paperclip 2.3.6 for members images.
class Image < ActiveRecord::Base
belongs_to :business
has_attached_file :data, :styles => {:normal => ["665x443#", :jpg],:thumb => ["104x50#", :jpg]}, :convert_options => {:all => "-channel RGB -strip -quality 80"}
end
Now for some reason the route for the images has changed to /system/data/ instead of previous /system/datas/
I cant figure out whats happened. All of the images still reside in /system/datas/ and a member uploaded some new images and Paperclip has put them in /system/data/
Anyone have any ideas whats happened? I cant figure it out. Thanks.
EDIT: Ok, I had a test box still on Rails 3.0.3. My Release version was on 3.0.7. If I roll back to Rails 3.0.3 it's working again - the path /system/data/ becomes /system/datas/ again.
Rails 3.0.5 introduced some additional inflections which meant 'datas' was no longer used as the plural of 'data', which broke the directory naming for Paperclip!
According to the paperclip documentation you can specify a path for saving images in your model using the :path option and providing a path.
You can find the documentation here.
I tried storing a local image in a rails console.
Because I have many pictures in my local storage (I use crawler to download tons of pictures), I want to store them into a database, with the benefit of paperclip to do some image job, like thumbnail etc.
If I use a webpage to save new pictures to database one by one, it will cost a lot of time. So I want to find a way in rails console (some code) that can batch save-picture-into-database.
To further clarify #andrea's answer:
YourPaperclippedModelHere.new(:your_paperclip_field => File.new(path, "r"))
So if your model is called Image and your paperclip field is data:
Image.new(:data => File.new(path_to_your_file, "r"))
If this is the model:
class User < ActiveRecord::Base
has_attached_file :avatar
end
then the following should work from the console:
>> User.create(:avatar => File.open('/path/to/image.jpg', 'rb'))
I dont know if it is what you want ... but
to save an paperclip asset from console
You could simple use a File instance .
a.e.
Image.new :data=>File.new("/path/to/image.jpg","r")
Late Answer but hopefully it will work for others.
You need to include.
File.new("#{Rails.root}/public/images/default_avatar.png", "r")
I wanted to run the callback after_post_process but it doesn't seem to work in Rails 3.0.1 using Paperclip 2.3.8. It gives an error:
undefined method `_post_process_callbacks' for #<Class:0x102d55ea0>
I want to call the Panda API after the file has been uploaded. I would have created my own processor for this, but as Panda handles the processing, and it can upload the files as well, and queue itself for an undetermined duration I thought a callback would do fine. But the callbacks don't seem to work in Rails3.
after_post_process :panda_create
def panda_create
video = Panda::Video.create(:source_url => mp3.url.gsub(/[?]\d*/,''), :profiles => "f4475446032025d7216226ad8987f8e9", :path_format => "blah/1234")
end
I tried require and include for paperclip in my model but it didn't seem to matter.
Anyideas?
Solution...
I put the callback after the paperclip has_attached in the given model and it works beautifully. I was just so used to always putting the callback at the top of all models that this didn't occur to me til later.
Moving the has_attached_file attribute above the validates_presence_of and validates_attachment
in your model still needs to be done it seems. I just ran into the same problem in my Rails 4/Ruby 2 implementation of PaperClip and putting it above fixed it.
I ran into this problem because the name of my paperclip image property did not match the name I was validating against.
as_attached_file :image
validates_attachment_content_type: :not_image