Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving files using Paperclip without upload

I had a quick question. Is it possible to save a file without actually uploading it through a form?

For example, let's say I'm looking at attachments from emails, and I want to save them using a paperclip. How do I do this? Do I manually have to call a save_file(or something similar) somewhere?

Any help would be much appreciated!

like image 289
Punit Rathore Avatar asked Nov 03 '09 11:11

Punit Rathore


2 Answers

I have a rake task that loads images (client logos) from a directory directly onto parperclip. You can probably adapt it to your needs.

This is my simplified Client model:

class Client < ActiveRecord::Base
  LOGO_STYLES = {
    :original => ['1024x768>', :jpg],
    :medium   => ['256x192#', :jpg],
    :small    => ['128x96#', :jpg]
  }

  has_attached_file :logo,
    :styles => Client::LOGO_STYLES,
    :url => "/clients/logo/:id.jpg?style=:style"
  attr_protected :logo_file_name, :logo_content_type, :logo_size

Then on my rake task I do this:

# the logos are in a folder with path logos_dir
Dir.glob(File.join(logos_dir,'*')).each do |logo_path|
  if File.basename(logo_path)[0]!= '.' and !File.directory? logo_path

    client_code = File.basename(logo_path, '.*') #filename without extension
    client = Client.find_by_code(client_code) #you could use the ids, too
    raise "could not find client for client_code #{client_code}" if client.nil?

    File.open(logo_path) do |f|
      client.logo = f # just assign the logo attribute to a file
      client.save
    end #file gets closed automatically here
  end
end

Regards!

like image 174
kikito Avatar answered Nov 05 '22 18:11

kikito


The file saved in Paperclip doesn't have to be uploaded directly through a form.

I'm using Paperclip in a project to save files from URLs from webcrawler results. I'm not sure how you'd get email attachments (are they on the local file system of the server? Is your app an email app like GMail?) but as long as you can get a file stream (via something like open(URI.parse(crawl_result)) in my case...) you can attach that file to your model field that's marked has_attached_file.

This blog post about Easy Upload via URL with Paperclip helped me figure this out.

Since it now appears the original blog post is no longer available - here's the gist of it pulled from wayback machine:

This example shows a Photo model that has an Image attachment.

The technique we're using requires adding a *_remote_url (string) column for your attachment, which is used to store the original URL. So, in this case, we need to add a column named image_remote_url the photos table.

# db/migrate/20081210200032_add_image_remote_url_to_photos.rb

class AddImageRemoteUrlToPhotos < ActiveRecord::Migration
  def self.up
    add_column :photos, :image_remote_url, :string
  end

  def self.down
    remove_column :photos, :image_remote_url
  end
end

Nothing special is required for the controller...

# app/controllers/photos_controller.rb

class PhotosController < ApplicationController

  def create
    @photo = Photo.new(params[:photo])
    if @photo.save
      redirect_to photos_path
    else
      render :action => 'new'
    end
  end

end

In the form, we add a text_field called :image_url, so people can upload a file or provide a URL...

# app/views/photos/new.html.erb

<%= error_messages_for :photo %>
<% form_for :photo, :html => { :multipart => true } do |f| %>
  Upload a photo: <%= f.file_field :image %><br>
  ...or provide a URL: <%= f.text_field :image_url %><br>
  <%= f.submit 'Submit' %>
<% end %>

The meaty stuff is in the Photo model. We need to require open-uri, add an attr_accessor :image_url, and do the normal has_attached_file stuff. Then, we add a before_validation callback to download the file in the image_url attribute (if provided) and save the original URL as image_remote_url. Finally, we do a validates_presence_of :image_remote_url, which allows us to rescue from the many exceptions that can be raised when attempting to download the file.

# app/models/photo.rb

require 'open-uri'

class Photo < ActiveRecord::Base

  attr_accessor :image_url

  has_attached_file :image # etc...

  before_validation :download_remote_image, :if => :image_url_provided?

  validates_presence_of :image_remote_url, :if => :image_url_provided?, :message => 'is invalid or inaccessible'

private

  def image_url_provided?
    !self.image_url.blank?
  end

  def download_remote_image
    self.image = do_download_remote_image
    self.image_remote_url = image_url
  end

  def do_download_remote_image
    io = open(URI.parse(image_url))
    def io.original_filename; base_uri.path.split('/').last; end
    io.original_filename.blank? ? nil : io
  rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
  end

end

Everything will work as normal, including the creation of thumbnails, etc. Plus, since we're doing all of the hard stuff in the model, "uploading" a file via URL works from within script/console as well:

$ script/console
Loading development environment (Rails 2.2.2)
>> Photo.new(:image_url => 'http://www.google.com/intl/en_ALL/images/logo.gif')
=> #<Photo image_file_name: "logo.gif", image_remote_url: "http://www.google.com/intl/en_ALL/images/logo.gif">
like image 23
Nate Avatar answered Nov 05 '22 19:11

Nate