利用paperclip实现图片上传
现在rails上最火的两大上传图片插件是fleximage与paperclip。如果单是处理图片,一气呵成的话,当然是fleximage,但如果还要上其他mp3,flv等附件,做成多态关联,那就选paperclip。嘛,在一般的功能上,paperclip还是比老一辈的上传插件要优胜不少,如什么acts_as_attachment,attachment_fu,还是更轻量化的file_column。
安装支持
本插件要有Rmagick与ImageMagick的外部支持,安装请参照我的另一篇文章。
安装paperclip
ruby script/plugin install git://github.com/thoughtbot/paperclip.git
打造主体框架
这里涉及到两个模块,User与Photo。 我们是利用user_id来区分相册。
mysql > create database album_development; rails album -d mysql cd album ruby script/plugin install git://github.com/technoweenie/restful-authentication.git ruby script/generate authenticated user sessions ruby script/generate scaffold Photo user:belongs_to is_avatar:boolean
自己配置config目录下的database.yml的用户名与密码。
接着下来的一步非常关键,我们要给Photo添加上传附件的能力。
ruby script/generate paperclip Photo image rake db:migrate
我们把附件的名字命名为image,这样Paperclip就会给我们Photo模型增加四个前缀为<attachment>_(我们刚才给予的附件的名字)的属性(<attachment> _file_name , <attachment> _file_size ,<attachment> _content_type ,与<attachment> _updated_at),也就是image_file_name,image_file_size,image_content_type与image_updated_at。
删除public目录下的index.html,并添加路由规则:
map.root :users
修改users_controller,添加index action
def index;end
添加对应视图
<%= link_to "相册",photos_path %>
修改_user_bar.html.erb
<div id="user_bar">
<% if logged_in? %>
<%= link_to "注销",logout_path, :title => "注销" %>
<%= link_to "欢迎,<strong>#{current_user.login}</strong>",current_user %>
<% else %>
<%= link_to "登录", login_path, :title => "登录" %>
<%= link_to "注册", signup_path, :title => "注册" %>
<% end %>
</div>
添加全局模板application.html.erb与全局助手layout_helper.rb
<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8"> <!-- simplified version; works on legacy browsers -->
<%= javascript_include_tag :defaults %>
<title><%= h(yield(:title) || controller.action_name ) %></title>
<%= stylesheet_link_tag 'blueprint','application' %>
<%= yield(:head) %>
</head>
<body>
<div id="container">
<%- flash.each do |name, msg| -%>
<%= content_tag :div, msg, :class => "#{name}" %>
<%- end -%>
<%- if show_title? -%>
<h1><%=h yield(:title) %></h1>
<%- end -%>
<%= render :partial => "users/user_bar" %>
<%= yield %>
</div>
</body>
</html>
module LayoutHelper
def title(page_title, show_title = true)
@content_for_title = page_title.to_s
@show_title = show_title
end
def show_title?
@show_title
end
end
修改application_controller
class ApplicationController < ActionController::Base
include AuthenticatedSystem
#令views能够调用各自的视图助手(helper)里的方法
helper :all
#令以下方法能在所有helper,views,controller中调用(来自restful-authentication插件)
helper_method :logged_in?, :current_user
#开启反CSRF (Cross-Site Request Forgery)攻击保护
protect_from_forgery
#过滤敏感字段
filter_parameter_logging :password, :password_confirmation
#发生错误时自动重定向页面
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
protected
def record_not_found
render :file => File.join(RAILS_ROOT, 'public', '404.html'), :status => 404
end
end
启动服务器,新建一名为司徒正美的用户,我们需要用其ID。
修改Photo模块,实现上传能力
新建_form.html.erb
<% form_for @photo, :html => { :multipart => true } do |f| %>
<%= f.error_messages %>
<% if logged_in? %>
<%= f.hidden_field :user_id,:value => current_user.id %>
<% end %>
<p>
<%= f.label :is_avatar %><br />
<%= f.check_box :is_avatar %>
</p>
<p>
<%= f.file_field :image %>
</p>
<p>
<button type="submit"><%= button_name %></button>
</p>
<% end %>
修改new.html.erb
<% title "上传图片" %>
<%= render :partial => 'form',:locals => {:button_name => "上传"} %>
<%= link_to 'Back', photos_path,:class => "button" %>
修改show.html.erb
<div class="figure"> <%= image_tag @photo.image.url ,:alt =>"被GFW和谐了!" %> <div class="legend">所有人:<%=h @photo.user.login %>;是否为头像:<%= @photo.is_avatar %></div> </div> <%= link_to '编辑', [:edit,@photo],:class => "button" %> <%= link_to '返回', photos_path,:class => "button" %>
修改photo.rb
class Photo < ActiveRecord::Base belongs_to :user has_attached_file :image end
这样它就可以运行了,非常简单!
通过url上传图片
ruby script/generate migration AddImageRemoteUrlToPhoto image_remote_url:string rake db:migrate
修改Photo模型
require 'open-uri'
class Photo < ActiveRecord::Base
belongs_to :user
has_attached_file :image
attr_accessor :image_url
before_validation :download_remote_image, :if => :image_url_provided?
validates_presence_of :image_remote_url, :if => :image_url_provided?, :message => '地址不合法'
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
修改_form.html.erb
<% form_for @photo, :html => { :multipart => true } do |f| %>
<%= f.error_messages %>
<% if logged_in? %>
<%= f.hidden_field :user_id,:value => current_user.id %>
<% end %>
<% if action_name == "new" %>
<p>
<%= f.label :is_avatar,"是否作为头像" %>
<%= f.check_box :is_avatar %>
</p>
<% end %>
<p>
<%= f.file_field :image %><br />
或者通过URL<%= f.text_field :image_url %>
</p>
<p>
<button type="submit"><%= button_name %></button>
</p>
<% end %>
一样上传成功!
添加多种样式与验证
#……………………
has_attached_file :image,
:default_url => "/images/rails.png",
:styles => {
:thumb=> "100x100#",
:gallery => "150x150>" ,
:avatar => "200x200>"}
#使用这个就不能删除图片了
#validates_attachment_presence :image
validates_attachment_size :image, :less_than => 5.megabytes
validates_attachment_content_type :image, :content_type => [ 'image/gif', 'image/png', 'image/x-png', 'image/jpeg', 'image/pjpeg', 'image/jpg']
#……………………
删除图片
由于paperclip默认是把上传的东西保存在硬盘中的,调用destroy action只能删除数据库的数据,但不能删除其关的图片。因此我们需要在其模型中添加删除图片的逻辑。
#=============================其他代码=====================
#=============================删除图片=====================
def delete_image=(value)
@delete_image = !value.to_i.zero?
end
def delete_image
!!@delete_image
end
alias_method :delete_image?, :delete_image
before_validation :clear_image
def clear_image
self.image = nil if delete_image? && !image.dirty?
end
#===============================其他代码===================
修改_form.html.erb
<% form_for @photo, :html => { :multipart => true } do |f| %>
<%= f.error_messages %>
<% if logged_in? %>
<%= f.hidden_field :user_id,:value => current_user.id %>
<% end %>
<% if action_name == "new" %>
<p>
<%= f.label :is_avatar,"是否作为头像" %>
<%= f.check_box :is_avatar %>
</p>
<% end %>
<p>
<%= f.file_field :image %><br />
或者通过URL<%= f.text_field :image_url %>
</p>
<%- unless @photo.new_record? || !@photo.image? -%>
<div>
<%= image_tag(@photo.image.url(:gallery), :alt => 'Photo', :title => '当前图片') %>
<p>
<%= f.label(:delete_image, '删除图片') %>
<%= f.check_box(:delete_image) %>
</p>
</div>
<%- end -%>
<p>
<button type="submit"><%= button_name %></button>
</p>
<% end %>
修改edit.html.erb
<% title "编辑图片" %>
<%= render :partial => 'form',:locals => {:button_name => "更新"} %>
<%= link_to '大图', @photo,:class => "button" %>
<%= link_to '返回', photos_path,:class => "button" %>
修改update action
def update
@photo = Photo.find(params[:id])
if @photo.update_attributes(params[:photo])
message = @photo.delete_image?? "删除图片成功!" : "更新图片成功!"
flash[:notice] = message
redirect_to(@photo)
else
render :action => "edit"
end
end
<h1>Listing photos</h1>
<table>
<tr>
<th>所有人</th>
<th>是否作为头像</th>
<th>预览</th>
</tr>
<% @photos.each do |photo| %>
<tr>
<td><%=h photo.user.login %></td>
<td><%= photo.is_avatar? "是":"否" %></td>
<td><%= image_tag photo.image.url(:thumb) ,:alt =>"被GFW和谐了!" %></td>
<td><%= link_to 'Show', photo %></td>
<td><%= link_to 'Edit', edit_photo_path(photo) %></td>
<td><%= link_to 'Destroy', photo, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New photo', new_photo_path %>
最后,paperclip是对window不友好的,google讨论组中最常见的问题是“is not recognized by the 'identify' command”错误,不过我在写这篇博文时还是没遇见过。它在LINUX环境是绝对没有问题的。
浙公网安备 33010602011771号