Rails 3 - Tempfile Path? - ruby-on-rails

I have the following:
attachments.each do |a|
Rails.logger.info a.filename
tempfile = Tempfile.new("#{a.filename}", "#{Rails.root.to_s}/tmp/")
Rails.logger.info tempfile.path
end
Where attachments is from paperclip.
Here's the output:
billgates.jpg
/Users/bhellman/Sites/cline/tmp/billgates.jpg20101204-17402-of0u9o-0
Why is the file name getting 20101204-17402-of0u9o-0 appended to at the end? That's breaking everything with paperclip etc. Anyone seen this before? For the life of I have no idea what's doing it?
Thanks
UPDATE
Paperclip: Paperclip on github
a is the attachment file
tempfile = Tempfile.new("#{a.filename}", "#{Rails.root.to_s}/tmp/")
tempfile << a.body
tempfile.puts
attachments.build(
:attachment => File.open(tempfile.path)
)

best make sure your tempfile has the correct extension, saving you to trying and change it after:
file = Tempfile.new(['hello', '.jpg'])
file.path # => something like: "/tmp/hello2843-8392-92849382--0.jpg"
more here: http://apidock.com/ruby/v1_9_3_125/Tempfile/new/class

The first argument for Tempfile.new is just a basename. To make sure each Tempfile is unique the characters are appended to the end of the file.

You should use Paperclip's API for this:
tempfiles = []
attachments.each do |a|
# use Attachment#to_file to get a :filesystem => file, :s3 => tempfile
tempfiles << a.to_file
end
tempfiles.each do |tf|
Rails.logger.debug tf.filename
end

attachment = attachments.build(
:attachment => File.open(tempfile.path)
)
# change the displayed file name stored in the db record here
attachment.attachment_file_name = a.filename # or whatever else you like
attachment.save!

The best way I found to deal with this was to specify the file extension in the Paperclip attribute. For example:
has_attached_file :picture,
:url => "/system/:hash.jpg",
:hash_secret => "long_secret_string",
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml"
Note that the :url is declared as '.jpg' rather than the traditional .:extension.
Good luck!

Related

Invalid encoding with rqrcode

I'm having an invalid encoding error that doesn't let me save the image to a carrierwave uploader.
require 'rqrcode_png'
img = RQRCode::QRCode.new( 'test', :size => 4, :level => :h ).to_img.to_s
img.valid_encoding?
=> false
I'm not sure if this is what you're looking for, in my case I needed to associate the generated QR code with a Rails model using carrierwave, what I ended up doing was saving the image to a temp file, associating that file with the model and afterwards deleting the temp file, here's my code:
def generate_qr_code!
tmp_path = Rails.root.join('tmp', "some-filename.png")
tmp_file = RQRCode::QRCode.new(self.hash_value).to_img.resize(200,200).save(tmp_path)
# Stream is handed closed, we need to reopen it
File.open(tmp_file.path) do |file|
self.qr_code = file
end
File.delete(tmp_file.path)
self.save!
end

Paperclip on S3 is changing file extensions from a url

I wrote a rake task that downloads an image from wikipedia given a celebrity name, but for some reason when storing on S3 the file extension is either being dropped or changed to .txt
The file otherwise is correct.
Any ideas?
From my celeb model:
has_attached_file :pic,
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:default_style => :medium,
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => "/:style/:img_name.:extension"
From my rake task:
desc "Update celeb pics from wiki"
task :update_celeb_pics => [:environment] do
include ApplicationHelper
Celeb.all.each do |celeb|
if !celeb.name.start_with?("(")
puts celeb.name
celeb.pic = open(getImage(celeb.name))
celeb.save
end
end
end
the getImage method is a helper that returns a string
require 'open-uri'
require 'uri'
module ApplicationHelper
def getInfo(name)
Nokogiri::XML(open(URI.escape("http://en.wikipedia.org/w/api.php?action=opensearch&search=#{name}&limit=1&namespace=0&format=xml")))
end
def nokoPage(name)
Nokogiri::XML(open(getURL(name)))
end
def getImage(name)
"http:" + nokoPage(name).css("table img")[0].attribute("src").value if !nokoPage(name).css("table img").empty?
end
def getDescription(name)
getInfo(name).css("Description").text
end
def getURL(name)
getInfo(name).css("Url").text
end
def getBday(name)
bday = nokoPage(name).css("span.bday")
return Date.parse(bday[0].text) if !bday.empty?
return Date.today
end
def getDday(name)
dday = nokoPage(name).css("span.dday")
return Date.parse(dday[0].text) if !dday.empty?
end
end
This is because
self.pic = open("http://something.com/bla/image.png")
is not the best solution here. Just yesterday, i got a Pull Request merged into Paperclip that lets you do this
self.pic = URI.parse(getImage(name))
This will ensure that your pic's content type matches the downloaded file, pic's filename is set to the name of the file downloaded.
The reason you get txt extension is because open returns a StringIO object which infact names the filename as "stringio.txt". Your filename is probably changed by the s3 code but the extension remains as '.txt'
I suggest you link your Gemfile to paperclip's github repo, run bundle and try again.
Cheers,
Aditya

PaperClip -- save a new attachment without deleting the old one

I am using Paperclip (w/ Amazon s3) on Rails 3. I want to attach a new file to my model without replacing the old file. I don't want the old file to be accessible, I only want to have it there on s3 as a back up.
Do you know if there is a way of telling paperclip to take care of it, itself?
in post.rb I have:
has_attached_file :sound,
:storage => :s3,
:s3_credentials => "....",
:styles => {:mp3 => {:format => :mp3}},
:processors => [:sound_processor],
:s3_host_alias => '....',
:bucket => '....',
:path => ":attachment/:id/:style/out.:extension",
:url => ":s3_alias_url"
and the processor is as follows:
class Paperclip::SoundProcessor < Paperclip::Processor
def initialize file, options = {}, attachment = nil
super
#format = options[:format] || "mp3"
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
end
def make
src = #file
dst = Tempfile.new([#basename,".#{#format}"])
dst.binmode
cmd = "ffmpeg -y -ab 128k -t 600 -i #{File.expand_path(src.path)} #{File.expand_path(dst.path)}"
Paperclip.log(cmd)
out = `#{cmd}`
raise Paperclip::PaperclipError, "processor does not accept the given audio file" unless $?.exitstatus == 0
dst
end
end
Here's what I do. I timestamp the filename before saving it (to prevent another file with the same name overwriting the original), and force paperclip to never delete anything.
before_create :timestamp_filename
def timestamp_filename
fname = Time.now.to_s(:db).gsub(/[^0-9]/,'') + '_' + sound_file_name
sound.instance_write(:file_name, fname)
end
# override paperclip's destroy files
# method to always keep them around
def destroy_attached_files
true
end
It's quite straightforward to add versioning to your models, recently I user CarrierWave and paper_trail in combo to achieve this. We were deploying to Heroku so S3 was in the mix too.
The reason I'm posting this as an answer is because, although controversial, I don't think libraries like PaperClip should support backing up files, a library specific to solve that problem feels better to me personally.
Here is a good resource that you can take a look at: http://eggsonbread.com/2009/07/23/file-versioning-in-ruby-on-rails-with-paperclip-acts_as_versioned/
It creates new versions of a file. Hope this helps.

Amazon S3 only accepting files with no spaces, no numbers in the title?

This is an odd question, but only commensurate with the strange behavior I'm seeing. My app is Rails 3/Paperclip/S3.
Symptoms:
All images are uploading regardless of their title.
When uploading a .pdf or .doc, if the title has no spaces like my_doc.pdf, it uploads fine.
When uploading a .pdf or .doc with spaces, such as My Doc.pdf, it fails, either with error broken pipe or by the file silently failing to upload to S3.
When uploading a .pdf or .doc with numbers, such as mydoc20.pdf, it also fails as above.
I imagine there are two possible solutions to this problem.
Fix the broken pipe error directly (preferred method).
Automatically rename every uploaded file to remove spaces and numbers before it is saved to S3 - while not fixing it at the source, I imagine this would allay the issue.
I would greatly appreciate any help you can give me in fixing 1 and/or 2.
Code
# Upload.rb model
class Upload < ActiveRecord::Base
has_attached_file :document,
:storage => :s3,
:s3_credentials => "#{::Rails.root.to_s}/config/s3.yml",
:path => "/docs/:style/:id/:basename.:extension"
has_attached_file :photo,
:styles => {:medium => "200x300>", :thumb => "100x150>" },
:storage => :s3,
:s3_credentials => "#{::Rails.root.to_s}/config/s3.yml",
:path => "/photos/:style/:id/:basename.:extension"
# s3.yml
development:
bucket: dev_bucket_name
access_key_id: dev_acc_key
secret_access_key: dev_sec_key
production:
bucket: my_production_bucket
access_key_id: my_access_key_id
secret_access_key: my_secret_key
# environment.rb is empty with regard to uploading.
# uploads_controller.rb
def edit
#candidate = Candidate.find(current_user.user_type_id)
render :layout => 'forms'
end
def update
#candidate = Candidate.find(params[:id])
if #candidate.update_attributes(params[:candidate])
flash[:notice] = "Profile updated successfully."
redirect_to :action => "show", :id => params[:id]
else
flash[:notice] = "There was an error updating your profile."
render :action => "edit", :id => params[:id]
end
end
I don't believe there are any methods involved. I almost hope there is something obviously wrong with my approach because that means it'll get fixed :).
For part two this should do it:
#s = "Really Important!*() Document version#123123.newest.pdf"
#s.gsub!(' ','_').downcase! #this will make everything lowercase and replace all spaces with underscores
#s.gsub!(/[^a-zA-Z._]+/,'') #this will remove all numbers and special characters except . and _
puts #s #prints "really_important_document_version.newest.pdf"
Edit: After some more research into paperclip I found the following: http://blog.wyeworks.com/2009/7/13/paperclip-file-rename
Check that link out, I believe it is what you are looking for.
Edit 2: In my initial read of your post I missed the part about pulling out numbers as well, I have modified the regulat expression code to account for that.

MIgrating data from EC2 to S3?

I am running a Rails app using Paperclip to take care of file attachments and image resizing, etc. The app is currently hosted on EngineYard cloud, and all attachments are stored in their EBS. Thinking about using S3 to handle all Paperclip attachments.
Does anyone know of a good and safe way for this migration? many thanks!
You could work up a rake task that iterates over your attachments and pushes each to S3. I used this one awhile back with attachment_fu -- wouldn't be too different. This uses the aws-s3 gem.
Basically the process is:
1. Select files from the database that need to be moved
2. Push them to S3
3. Update database to reflect that the file is no longer stored locally (this way you can do them in batches and don't need to worry about pushing the same file twice).
#attachments = Attachment.stored_locally
#attachments.each do |attachment|
base_path = RAILS_ROOT + '/public/assets/'
attachment_folder = ((attachment.respond_to?(:parent_id) && attachment.parent_id) || attachment.id).to_s
full_filename = File.join(base_path, ("%08d" % attachment_folder).scan(/..../), attachment.filename)
require 'aws/s3'
AWS::S3::Base.establish_connection!(
:access_key_id => S3_CONFIG[:access_key_id],
:secret_access_key => S3_CONFIG[:secret_access_key]
)
AWS::S3::S3Object.store(
'assets/' + attachment_folder + '/' + attachment.filename,
File.open(full_filename),
S3_CONFIG[:bucket_name],
:content_type => attachment.content_type,
:access => :private
)
if AWS::S3::Service.response.success?
# Update the database
attachment.update_attribute(:stored_on_s3, true)
# Remove the file on the local filesystem
FileUtils.rm full_filename
# Remove directory also if it is now empty
Dir.rmdir(File.dirname(full_filename)) if (Dir.entries(File.dirname(full_filename))-['.','..']).empty?
else
puts "There was a problem uploading " + full_filename
end
end
I found myself in the same situation and took bensie's code and made it work for myself - this is what I came up with:
require 'aws/s3'
# Ensure you do the following:
# export AMAZON_ACCESS_KEY_ID='your-access-key'
# export AMAZON_SECRET_ACCESS_KEY='your-secret-word-thingy'
AWS::S3::Base.establish_connection!
#failed = []
#attachments = Asset.all # Asset paperclip attachment is: has_attached_file :attachment....
#attachments.each do |asset|
begin
puts "Processing #{asset.id}"
base_path = RAILS_ROOT + '/public/'
attachment_folder = ((asset.respond_to?(:parent_id) && asset.parent_id) || asset.id).to_s
styles = asset.attachment.styles.keys
styles << :original
styles.each do |style|
full_filename = File.join(base_path, asset.attachment.url(style, false))
AWS::S3::S3Object.store(
'attachments/' + attachment_folder + '/' + style.to_s + "/" + asset.attachment_file_name,
File.open(full_filename),
"swellnet-assets",
:content_type => asset.attachment_content_type,
:access => (style == :original ? :private : :public_read)
)
if AWS::S3::Service.response.success?
puts "Stored #{asset.id}[#{style.to_s}] on S3..."
else
puts "There was a problem uploading " + full_filename
end
end
rescue
puts "Error with #{asset.id}"
#failed << asset.id
end
end
puts "Failed uploads: #{#failed.join(", ")}" unless #failed.empty?
Of course, if you have multiple models you will need to adjust as necessary...

Resources