Paperclip save attachment - ruby-on-rails

Is there a better way to save some string as an attachment via Paperlip as making a tmp file, putting the string into it, opening it again and saving it as an attachment ?
Like this :
def save_string data
tmp_file = "/some/path"
File.open(tmp_file,'w') do |f|
f.write(data)
end
File.open(tmp_file,'r') do |f|
ceneo_xml = f
save!
end
end

There is actually a better way - you can wrap it to StringIO which Paperclip enhances and you will get a pseudo uploaded file in no time. You can customize it by defining instance methods or directly create a subclass of StringIO like this
class InvoiceAttachment < StringIO
def initialize(invoice, content)
#invoice = invoice
super(content)
end
def original_filename
from = #invoice.from
to = #invoice.to
date = #invoice.created_at.strftime('%B-%Y').downcase
"invoice_#{date}_from_#{from}_to_#{to}.pdf"
end
def content_type
'application/pdf'
end
end
Enjoy!

Paperclip stores files alongside your models -- this is what it has been written to do, so I think the short answer is "no".
If you look in attachment.rb in the Paperclip source you'll see a method called def assign uploaded_file. If you look at the implementation of this method you can see that it expects the uploaded file object to have a certain methods defined on it.
You could create your own class which followed the same interface as Paperclip expects, but to be honest your solution of saving a file and assigning that to Paperclip is probably the easiest approach.

Related

In CarrierWave, what does retrieve_from_store! do?

The documentation says about retrieve_from_store!:
Retrieves the file from the storage.
But when I call the method, rather than getting something like a file, I just get an array returned:
irb(main):008:0> uploader.retrieve_from_store!('my_file.png')
=> [:retrieve_versions_from_store!]
What exactly does the method do?
I was looking for the same thing today. Found something from Jonas Nicklas in the CarrierWave forum here...
retrieve_from_store! changes the state of the uploader, it
doesn't return anything sensible. You want to do this:
uploader.retrieve_from_store!('test.jpg')
uploader.do_whatever
The return value from retrieve_from_store! is irrelevant.
I just needed to use model.uploader.read to get to the bytes. It seems, uploader delegates to the file via Proxy: https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/uploader/proxy.rb#L43
Carrierwave is not designed to work without model.
class DocumentUploader < CarrierWave::Uploader::Base
storage :file
def store!(*args)
super
#file
end
def filename
SecureRandom.uuid
end
end
file = uploader.store! file
puts file.path # ok
You can patch retrieve_from_store in the same way.
It will be tricky to retrieve important info for other methods, so please use another uploader if you can.

how to find url from id and name in carrierwave

I have model message, and attach file
class Message
has_one :attach_file
end
class AttachFile
mount_uploader :path, FileUploader
end
class FileUploader
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
I have controller list messages with attach file.
class Controller
def index
message = Message.join(:attach_file).select('messages.*, attach_files.*')
render :json => message
end
end
I try many way to retrieve attach file url, it works on public bucket because i set url from bucket name, id, name attach file. In case of private public, it require access key and signure, expires.
Is there any carrierwave's way to find attach file url
You have two problems here:
If you're using select in this way you have no access to CarrierWave's methods on the associated model. select in general has very few use cases; you probably shouldn't use it.
render json ignores associations unless you tell it to include them (or you've already overridden the model's as_json, or you're using a custom serializer, etc...).
A bonus semi-problem is that join isn't really what you want here. The idiomatic way to avoid N + 1 queries is includes.
def index
messages = Message.includes(:attach_file)
render json: messages, include: :attach_file
end
Even better than include: :attach_file would be to use a tool like active_model_serializers to let the view layer handle your json serialization, but this answer's already long enough as it is.
Try:
message = Message.find params[:id]
file_url = message.attach_file.path.url

How to access the result returned by processor#make in after_post_process callback in Paperclip?

I am trying to allow uploading pdf file. When the pdf file is uploaded, a Paperclip::Processor will convert it into a text file in its make instance method. Then, I would like to parse the text file to populate ActiveRecord class. How can I access the processed file returned by the Processor in its make instance method?
The code skeleton is here:
module Paperclip
class PdfToText < Processor
...
def make
dst = File.new(..)
#do something to dst
dst
end
end
end
class Xyz < ActiveRecord::Base
has_attached_file :source_file, ..., :processors =>[:pdftotext]
after_post_process :do_it
def do_it
# I would like to access the dst file returned in make of the processor PdfToText
end
end
Appreciate your thoughts.
What about this?
source_file.queued_for_write[:original].path

Displaying a Carrierwave filename in the view

I am trying to display the filename of a Carrierwave attachment in a Rails erb template. The following does not work:
<%= #page.form.filename %>
This seems in line with the documentation. Is some additional step needed?
My page model looks like this:
class Page < ActiveRecord::Base
mount_uploader :form, FormUploader
end
The form uploader looks like this:
class FormUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(pdf)
end
end
I have been able to get the filename via the file internal parameter:
<%= #page.form.file.filename %>
The documentation you're looking at is the sanitized file, it's what it uses for actually storing a file. The part you're looking for is FormUploader, which is an Uploader, and part of http://rubydoc.info/gems/carrierwave/0.5.2/CarrierWave/Uploader
If you want to get the file name, you could either read it from the database column directly, or use File.basename(#page.form.path) to extract it easily.
The Carrierwave docs might be a bit off, but recommended way seems to be:
#page.form.file.identifier
#adamonduty's solution is great. Another solution I used before, just create a method on the model:
def name
file.path.split("/").last
end
You're right #epylinkn. Documentation points towards using:
#page.form.file.identifier
But when I use that, I always get nil (just as #Cheng commented).
I then inspected my objects methods (#page.form.file.methods.inspect), and found the following to work:
#page.form.file_identifier
In your model's associated uploader class, define a filename method.
def filename
File.basename(path)
end
You can then call
model_instance.file.filename
Works as of CarrierWave 1.1.0. This is a succinct restatement/amalgamation of kikito and Chris Alley's responses above.
If you're using ActiveRecord, you can directly access the field named form in two ways:
def my_method
self[:form]
end
or
def my_method
form_before_type_cast
end
The second method is read-only.
CarrierWave::SanitizedFile has a private original_filename method containing the filename of the uploaded file. (docs: http://rdoc.info/github/jnicklas/carrierwave/master/CarrierWave/SanitizedFile:original_filename)
After reading through this thread from the CarrierWave mailing list, none seemed to fit my needs. With something like
class Upload < ActiveRecord::Base
mount_uploader :file, FileUploader
# ...
I heavily modify the :file column value from the original filename. Due to this I decided to track the original filename in a separate column from the one bound to CarrierWave. In my FileUploader I simply added a reader that wraps the private original_filename method:
def original_file
original_filename
end
I then added a before_create event to the Upload class (my Upload records are never modified, so a before_create is acceptable for my needs)
before_create do
self.original_file = self.file.original_file
end
I'm assuming you've got models like this?
class Page
mount_uploader :form, FormUploader
end
If so you should be able to call:
#page.form.url
#page.form.filename
Are you sure you've uploaded/attached the file correctly? What do you see when you inspect #page.form? Remember, the attachment will not be saved until you've fully processed the upload.
This is my solution:
before_save :update_file_attributes
def update_file_attributes
if file.present? && file_changed?
self.content_type = file.file.content_type
self.file_size = file.file.size
self.file_name = read_attribute(:file)
end
end

Rails: Saving the contents of a binary field to a file

I have a model with a binary field that contains a file. I'd like to save this file to disk as part of a process I need to do. For some reason, I can't find anything on how to do this.
The model contains a filename field and a file_contents field. I'd like to do something like this:
model = SomeModel.find :first
model.file_contents.save_to_file(model.filename)
Any help would be appreciated!
I don't know why you'd want to call #save_to_file on the file contents instead of the model. Since you defined file_contents as an AR attribute I guess you want to save it to the DB and save it to disk. If that's the case you can simply add a method like this to your model:
class YourModel < ActiveRecord::Base
# ... your stuff ...
def save_to_file
File.open(filename, "w") do |f|
f.write(file_contents)
end
end
end
And then you'd simply do:
obj = YourModel.find(:first)
obj.save_to_file
In ActiveRecord, the :binary type that you use to define your column type in your migration is going to map to a blob type in the database. So that wouldn't allow you to save to a file.
I think you would need to define a model class that is not a subclass of ActiveRecord::Base and define a custom save_to_file method for that class using the file i/o support in Ruby (the IO class and its subclass, File).
class SomeModel
attr_accessor :file
attr_accessor :contents
def initialize
#file = File.new("file.xyz", "w")
end
def save_and_close
#file << contents
#file.close
end
end

Resources