Rails - Using Tempfile to write on Heroku? - ruby-on-rails

I need to be able to write a temporary file for use during the request only.
Locally I can use the following successfully:
tempfile = File.open(a.original_filename,'w')
tempfile.write_nonblock(a.body)
paperclip stuff........
tempfile.close
That works great, but not on Heroku... How can I do the above with Heroku's restrictions: link text
I'm not understanding how to translate the above into: #{RAILS_ROOT}/tmp/myfile_#{Process.pid}
Thanks for any help you can provide here.

Did you try tempfile = File.open("#{RAILS_ROOT}/tmp/myfile_#{Process.pid}",'w') ?
The correct syntax is tempfile = File.new("#{RAILS_ROOT}/tmp/myfile_#{Process.pid}",'w') (see comments)

I have a solution for you if you have to do stuff with paperclip. Include this class in your lib folder as heroku_compliant_file.rb
class HerokuCompliantFile < StringIO
def initialize(str,filename,content_type)
#original_filename = filename
#content_type = content_type
super(str)
end
end
In your model containing the paperclip -
def insert_a_hunk_of_string_into_paperclip(some_object)
f = HerokuCompliantFile.new(some_object.render,"myfile_#{Process.pid}","application/pdf")
self.paperclip_model = f
save
end

Related

Rails tag_id3v1 mp3

Rails 4.2 ap and taglib-ruby gem
I have tried a few approaches to tag my audio files using taglib-ruby.
I have tried as a carrierwave process, and now, I am trying to tag after_save.
My question, if I am doing a callback after_save:
def tag_id3v1(tags)
TagLib::MPEG::File.open(file.path) do |file|
tag = file.id3v1_tag(true)
tag.title = :title
file.save
end
end
what should my file path be? I have tried :file, :file_name, and the uploader version url, #{track.mp3.url}
I am trying to reopen the saved files and write the tags to the files. Does anyone have any hints on the best approach to do this?
Finally
TagLib::MPEG::File.open(file.file) do |file|
Always something like, "file.file". No matter the approach, that is what did the trick for me.
I ended up doing this in a carrierwave callback inside of a :version. Final code
version :mp3 do
process :convert => [:mp3]
def full_filename(for_file)
"#{super.chomp(File.extname(super))}.mp3"
end
after :store, :tag_id3v2
end
def tag_id3v2(for_file)
TagLib::MPEG::File.open(file.file) do |file|
tag = file.id3v2_tag(true)
tag.title = "#{model.title}"
file.save
end
end
(...)

Open the temporary file before it is saved in Rails model

Once you've uploaded a file, is it possible to open that file before the model is saved?
I'm using Paperclip to save files in the /public folder.
class Book < ActiveRecord::Base
before_save :open_it
has_attached_file :upload
def open_it
a_file = open(upload.url) # this doesn't work before save ?
# ...
end
end
found it:
def model_method
f = open(self.upload.queued_for_write[:original].url)
end
Update:
Based on response from ecoologic, use .path instead of .url for more recent versions of the Paperclip gem
Close enough, but .url didn't work for me, path worked.
file = open(uploaded.queued_for_write[:original].path)
did the trick.
P.S.: Don't forget to close it in an ensure block.

paperclip renaming stringio.txt using instance_write

I am using omniauth-facebook and trying to get the profile picture to show up using paperclip. Here is the code I use to get the picture to my User model
def picture_from_url(url)
self.profile_pic =open(url)
end
However, it always saves as stringio.txt. So searching on this behavior I found out that paperclip allows for callbacks so I wrote the following method in the User model to rename the filename
def rename_profile_pic
self.profile_pic.instance_write :filename, "#{self.username}.jpg"
end
and passed it to the callback
before_post_process :rename_profile_pic
But this doesn't seem to help.
Any ideas how i can fix this ?
thanks
In case you haven't found the solution yet:
data = StringIO.new(file_data)
data.class.class_eval { attr_accessor :original_filename, :content_type }
data.content_type = content_type
data.original_filename = file_name
object.attachment = data
Convert your stringio.txt to file using this:
file = StringIO.new(obj)
file.original_filename = "#{self.username}.jpg"
and then assign your file to profile_pic
My solution for creating files from a string:
class FileIO < StringIO
def initialize(content:, filename:)
super(content)
#original_filename = filename
end
attr_reader :original_filename
end
FileIO.new(content: obj, filename: "#{username}.jpg")
This helped me solve the problem with the Carrierwave error when saving the file:
TypeError: no implicit conversion of nil into String

Renaming uploaded files with Carrierwave

I'm using Carrierwave to upload files, and I have it working.
My issue is attempting to change the name of the uploaded file.
In the generated uploader.rb there is a method I think I should be using
def filename
"something.jpg" if original_filename
basename = "what"+orginal_filename if original_filename, works
basename = (0...8).map{65.+(rand(25)).chr}.join if original_filename # will create a random name for each version, e.g. the orginal, the thumb, and the filename in the db, useless
end
I can't seem to access items like 'extension' or 'content_type' in sanitized_file.rb, so this is a bit beyond my current skill level right now.
Any suggestions or exercises for doing this, i.e. generate filename for an uploaded file that works as well as the carrierwave default (do nothing, but does carry on to each version)? Seems like it should be simple enough but I've stumbled over this.
Well, another problem with your random filename generator is that it's possible to have collisions isn't it? You could possibly generate a filename that was already generated.
One way to go about it would be to somehow generate a hash based on unique properties of the image, like file path. An example, from the carrierwave group:
def filename
if original_filename
#name ||= Digest::MD5.hexdigest(File.dirname(current_path))
"#{#name}.#{file.extension}"
end
end
This will create an MD5 hash based on the current path and then append the original file's extension to it.
Edit: The carrierwave wiki added an entry with a few methods on how to create random and unique filenames for all versioned files.
To have a real unique filename (not almost unique) I recommend to use the uuid gem.
in Gemfile add:
gem 'uuid'
in file_uploader.rb:
def filename
if original_filename
if model && model.read_attribute(mounted_as).present?
model.read_attribute(mounted_as)
else
#name ||= "#{mounted_as}-#{uuid}.#{file.extension}"
end
end
end
protected
def uuid
UUID.state_file = false
uuid = UUID.new
uuid.generate
end
From the Google Group:
def filename
#name ||= "#{secure_token}.#{file.extension}" if original_filename
end
private
def secure_token
ivar = "##{mounted_as}_secure_token"
token = model.instance_variable_get(ivar)
token ||= model.instance_variable_set(ivar, ActiveSupport::SecureRandom.hex(4))
end
To just make the record.id prefix the filename you can do the following:
class MyUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
model.class.to_s.underscore.pluralize
end
def filename
model.id ? "#{model.id}-#{original_filename}" : original_filename
end
def url
"/#{store_dir}/#{model.id}-#{model.file_before_type_cast}"
end
end
The other solution looks good, but how I did it then was to have a hook that created a random string for a new name on instance creation, then:
def filename
"#{model.randomstring}.#{model.image.file.extension}"
end
in the uploader.
That worked, putting the random name generation as part of the model, then having carrierwave use that.
I am curious which is faster, more effective, reasonable, sound, etc.
Here is the solution, how to change the name of the file, if store_dir already contains the file with the exact name:
if File.exists?(Rails.root.join("documents/" + "#{file.filename}")) && !path.to_s.eql?(Rails.root.join("documents/" + original_filename).to_s)
#name ||= File.basename(original_filename, '.*') + Digest::MD5.hexdigest(File.dirname(current_path)).from(25)
"#{#name}.#{file.extension}"
else
"#{original_filename}"
end
Note: Rails.root.join("documents/") is defined as my store_dir.
Hope it helps someone.

Paperclip save attachment

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.

Resources