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!
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!
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">
Related
How can I download an internet image from a URL and save it in locally in Rails?
i give a form to add image url form link if i submit that form i want to download image to computer local disk (Home/Documents/food_img)
pls need help
in downloads controller
class DownloadsController < ApplicationController
def index
#downloads = Download.all
#download = Download.new
end
def create
#download = Download.create(user_params)
end
private
def user_params
params.require(:download).permit(:image,:image_url)
end
end
in view/index.html.erb
<%= form_for(#download, url: downloads_path) do |f| %>
<%= f.text_field :image_url %>
<%=f.submit%>
<% end %>
in model
class Download < ApplicationRecord
has_attached_file :image, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/
require 'open-uri'
download = open('http://www.shopprod.com/assets/photos/ishop-718755d2bc62956994c867681b2622e77b4c9af3d1ecd6fa856127b704a459b2.png')
IO.copy_stream(download, "~/#{download.base_uri.to_s.split('/')[-1]}")
end
in db
t.string: image_url
If you are using ActiveStorage (which I would recommend) you can use .attach to attach an IO stream to a record.
given:
class Download < ApplicationRecord
has_one_attached :image
end
From the rails console you can download a file and attach it with:
require 'open-uri'
uri = 'http://www.shopprod.com/assets/photos/ishop-718755d2bc62956994c867681b2622e77b4c9af3d1ecd6fa856127b704a459b2.png'
download = Download.new
download.image.attach(io: open(uri), filename: uri.split('/').last)
However you can't just toss it whilly-nilly into your model class and expect it to work. The class body is executed when the class is read.
Instead you need to put it into a method and call it at the correct point.
class Download < ApplicationRecord
has_one_attached :image
def download_image!
require 'open-uri'
image.attach(
io: open(self.image_url),
filename: self.image_url.split('/').last
)
end
end
class DownloadsController < ApplicationController
def create
#download = Download.new(download_params)
if !#download.image.attached? && #download.image_url
#download.download_image!
# #todo handle errors
end
if #download.save
redirect_to #download
else
render :new
end
end
def download_params
params.require(:download)
.permit(:image, :image_url)
end
end
I'm trying to upload images through carrier wave, and using rails.
using minimagick.
Actually, the uploads working properly I think, but images are not shown up.
Also it shows nil value in database.
here's my code.
app/models/user.rb
class User < ActiveRecord::Base
mount_uploader :image, ImageUploader
end
mypage.html.erb (form action to "edit_complete")
<input type="file" name='image'>
users_controller.rb
def edit_complete
user = User.find(session[:user_id])
user.image = params[:image]
user.save
redirect_to :back
end
app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
No errors appeared, but nothing saved in database.
I expect image files should be saved in public/upload folder, and image file should be shown in database in rails c User.all.
However, when I upload 1.png file,
there are nothing saved, and public/upload folder is not generated.
Also in database shows nil value.
Is there any solution for this?
In file upload You need to use multipart option in form tag ie multipart option should be true in form tag.
Try something like this:
<%= form_for #user, :html => {:multipart => true} do |f| %>
<%= f.file_field :image%>
<%= f.submit 'Edit'%>
<% end %>
I am very new to ROR. I have a task to finish:
Here's the Model:
class File::DataImport < ActiveRecord::Base
attr_accessible :created_by, :file_name, :file_source, :updated_at, :updated_by
end
Here's the Controller:
class Files::DataImportsController < ApplicationController
def index
end
def new
end
end
And the views I have are index and new.
I want a field to upload data. The data should be stored in the server and save the filepath into the database in a specified column file_name. The path should be default to all uploading files.
I am stuck with how to start. Please help me to find the solution and I will learn from this.
Thanks in advance.
db/migrate/20110711000004_create_files.rb
class CreateFiles < ActiveRecord::Migration
def change
create_table :files do |t|
t.string :name
# If using MySQL, blobs default to 64k, so we have to give
# an explicit size to extend them
t.binary :data, :limit => 1.megabyte
end
end
end
app/controllers/upload_controller.rb
class UploadController < ApplicationController
def get
#file = File.new
end
end
app/views/upload/get.html.erb
<% form_for(:file,
url: {action: 'save'},
html: {multipart: true}) do |form| %>
Upload your file: <%= form.file_field("uploaded_file") %><br/>
<%= submit_tag("Upload file") %>
<% end %>
app/models/file.rb
class File < ActiveRecord::Base
def uploaded_file=(file_field)
self.name = base_part_of(file_field.original_filename)
self.data = file_field.read
end
def base_part_of(file_name)
File.basename(file_name).gsub(/[^\w._-]/, '')
end
end
app/controllers/upload_controller.rb
def save
#file = File.new(params[:file])
if #file.save
redirect_to(action: 'show', id: #file.id)
else
render(action: :get)
end
end
app/controllers/upload_controller.rb
def file
#file = File.find(params[:id])
send_data(#File.data,
filename: #File.name,
disposition: "inline")
end
app/controllers/upload_controller.rb
def show
#file = File.find(params[:id])
end
app/views/upload/show.html.erb
<h3><%= #file.name %></h3>
<img src="<%= url_for(:action => 'file', :id => #file.id) %>"/>
you should consider using one of the already available solutions like paperclip: https://github.com/thoughtbot/paperclip or carrierwave: https://github.com/jnicklas/carrierwave
Besides the Readmes there are also good tutorials out there:
http://railscasts.com/episodes/134-paperclip
http://railscasts.com/episodes/253-carrierwave-file-uploads
edit: As you want to implement it yourself I recommend examining the sources of the above on Github and try to understand what their code is doing. Also I would not bother implementing it myself, but if you have your reasons this might get you going..
You might want to look into a solution such as carrierwave.
The Github page provides a good explanation on how to use it, but this is also a nice guide.
I have a preview of attached image in my update form. The problem appears when user gets validation errors on attachment field. In this case image thumbnail url becomes as if an image was uploaded without any errors (it shows name of file that was not saved at server).
Here is how I get image url in my view:
<%= image_tag(#product.photo.url(:medium)) %>.
Controller:
def update
#product = Product.find(params[:id])
#product.update_attributes(params[:product]) ? redirect_to('/admin') : render(:new)
end
def edit
#product = Product.find(params[:id])
render :new
end
Model:
class Product < ActiveRecord::Base
<...>
##image_sizes = {:big => '500x500>', :medium => '200x200>', :thumb=> '100x100>'}
has_attached_file :photo, :styles => ##image_sizes, :whiny => false
validates_attachment_presence :photo
validates_attachment_content_type :photo, :content_type => ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'], :message => I18n.t(:invalid_image_type)
validates_attachment_size :photo, :less_than => 1.megabytes, :message => I18n.t(:invalid_image_size, :max => '1 Mb')
after_post_process :save_image_dimensions
<...>
end
UPD: The simpliest solution is to add #photo_file_url = #product.photo.url(:medium) in controller's update action before #product.update_attributes and <% #photo_file_url ||= #product.photo.url(:medium) %> in the view.
I actually had the same issue and here is what I had done (which seems a bit of a hack) but works and will show the default or previous image on update failure
after_validation :logo_reverted?
def logo_reverted?
unless self.errors[:logo_file_size].blank? or self.errors[:logo_content_type].blank?
self.logo.instance_write(:file_name, self.logo_file_name_was)
self.logo.instance_write(:file_size, self.logo_file_size_was)
self.logo.instance_write(:content_type, self.logo_content_type_was)
end
end
In my app I had similar issues, so I used:
<%= image_tag(#product.photo.url(:medium)) if #photo.valid? %>
That way nothing shows up if the Photo isn't valid (ie successfully created), but when you edit existing records the image is shown.
Hope that helps.
I am working with Rails 3 and Paperclip to attach uploaded files to several object types using a polymorphic association. I have created an Asset model and an inherited Image model (will be adding others, like Video and Documents later) as follows:
# app/models/asset.rb
class Asset < ActiveRecord::Base
# Nothing here yet
end
# app/models/image.rb
class Image < Asset
belongs_to :assetable, :polymorphic => true
has_attached_file :file, {
:styles => {
:small => { :geometry => '23x23#', :format => 'png' },
:medium => { :geometry => '100x100#', :format => 'png' } }
}.merge(PAPERCLIP_STORAGE_OPTIONS).merge(PAPERCLIP_STORAGE_OPTIONS_ASSET_IMAGE) # Variables sent in environments to direct uploads to filesystem storage in development.rb and S3 in production.rb
validates_attachment_presence :file
validates_attachment_size :file, :less_than => 5.megabytes
end
I then have another object type, Unit, which I am attaching multiple images to as follows:
# app/models/unit.rb
class Unit < ActiveRecord::Base
# ...
has_many :images, :as => :assetable, :dependent => :destroy
accepts_nested_attributes_for :images
end
# app/controllers/units_controller.rb
class UnitsController < ApplicationController
# ...
def new
#unit = current_user.units.new
# ...
#unit.images.build
end
def create
#unit = current_user.units.new(params[:unit])
# ...
respond_to do |format|
if #unit.save
format.html { redirect_to(#unit, :notice => 'Unit creation successful!') }
else
format.html { render :action => "new" }
end
end
end
def show
#unit = current_user.units.find(params[:id])
#unit_images = #unit.images
# ...
end
def edit
#unit = current_user.units.find(params[:id])
# ...
#unit.images.build
end
def update
#unit = current_user.units.find(params[:id], :readonly => false)
respond_to do |format|
if #unit.update_attributes(params[:unit])
format.html { redirect_to(#unit, :notice => 'Unit was successfully updated.') }
else
format.html { render :action => "edit" }
end
end
end
def destroy
#unit = current_user.units.find(params[:id])
#unit.destroy
respond_to do |format|
format.html { redirect_to(units_url) }
end
end
end
# app/views/units/_form.html.haml
.field # Display already uploaded images
= f.fields_for :images do |assets|
- unless assets.object.new_record?
= link_to(image_tag(assets.object.file.url(:medium)), assets.object.file.url(:original))
.field # Display field to add new image
= f.fields_for :images do |assets|
- if assets.object.new_record?
= assets.label :images, "Image File"
= assets.file_field :file, :class => 'uploadify'
Using these settings I am able to upload images one at at time, per display of the form.
The issues start when I try to integrate Uploadify to add multi file uploading/previewing. I have satisfied all the Uploadify dependancies, but in order to save the images associated with the Unit model I need to somehow include a reverence to the unit_id so that the polymorphic association can be made properly. Below is my current Uploadify code:
%script
$(document).ready(function() {
$('.uploadify').uploadify({
uploader : '/uploadify/uploadify.swf',
cancelImg : '/uploadify/cancel.png',
auto : true,
multi : true,
script : '#{units_path}',
scriptData : {
"#{key = Rails.application.config.session_options[:key]}" : "#{cookies[key]}",
"#{request_forgery_protection_token}" : "#{form_authenticity_token}",
}
});
});
So while I can easily upload with Paperclip along, Uploadify will not work. Any help would be much appreciated. Thank you in advance.
UPDATE:
After doing more research I ran across this comment to a similar issue: Rails3, S3, Paperclip Attachment as it's own model?. Any thoughts on whether or not that would work in this situation? Is there an easy way of determining the unit.id from the /new method and passing it to the Uploadify-created Asset?
We had solved a very similar problem once by saving the model right away when loading the form in a draft state (using state machine). Like this the model is available when you're trying to attach the files you're uploading and once you're submitting the rest of the form, you're basically just updating the model which changes it's state to e.g. published. It's a little work to update the controllers etc., but it did the trick.