使用Rails和Dragonfly上传文件
不久前,我写了一篇文章“使用Rails和Shrine上传文件 ”,其中介绍了如何借助Shrine gem将文件上传功能引入Rails应用程序。 但是,有很多类似的解决方案可用,而我最喜欢的之一是Dragonfly ,这是Mark Evans为Rails和Rack创建的易于使用的上传解决方案。
去年年初 ,我们讨论了这个库,但与大多数软件一样,它有助于不时查看库,以了解更改的内容以及如何在应用程序中使用它。
在本文中,我将指导您完成蜻蜓的设置并解释如何利用其主要功能。 你将学到如何:
- 将Dragonfly集成到您的应用程序中
- 配置模型以与Dragonfly一起使用
- 介绍基本的上传机制
- 介绍验证
- 生成图像缩略图
- 执行文件处理
- 存储上载文件的元数据
- 准备要部署的应用程序
为了使事情变得更有趣,我们将创建一个小型音乐应用程序。 它将展示可以在网站上管理和播放的专辑和相关歌曲。
GitHub上提供了本文的源代码。 您还可以签出该应用程序的工作演示 。
列出和管理相册
首先,创建一个没有默认测试套件的新Rails应用程序:
rails new UploadingWithDragonfly -T
对于本文,我将使用Rails 5,但是大多数描述的概念也适用于旧版本。
创建模型,控制器和路径
我们的小型音乐网站将包含两个模型: Album
和Song
。 现在,让我们用以下字段创建第一个:
title
(string
)-包含相册的标题singer
(string
)—专辑的表演者image_uid
(string
)—用于存储相册的预览图像的特殊字段。 您可以根据自己的喜好命名此字段,但必须按照Dragonfly文档的说明包含_uid
后缀。
创建并应用相应的迁移:
rails g model Album title:string singer:string image_uid:string
rails db:migrate
现在,让我们创建一个非常通用的控制器,以使用所有默认操作来管理相册:
albums_controller.rb
class AlbumsController < ApplicationControllerdef index@albums = Album.allenddef show@album = Album.find(params[:id])enddef new@album = Album.newenddef create@album = Album.new(album_params)if @album.saveflash[:success] = 'Album added!'redirect_to albums_pathelserender :newendenddef edit@album = Album.find(params[:id])enddef update@album = Album.find(params[:id])if @album.update_attributes(album_params)flash[:success] = 'Album updated!'redirect_to albums_pathelserender :editendenddef destroy@album = Album.find(params[:id])@album.destroyflash[:success] = 'Album removed!'redirect_to albums_pathendprivatedef album_paramsparams.require(:album).permit(:title, :singer)end
end
最后,添加路线:
config / routes.rb
resources :albums
整合蜻蜓
是时候蜻蜓成为众人瞩目的时候了。 首先,将gem添加到Gemfile中 :
宝石文件
gem 'dragonfly'
跑:
bundle install
rails generate dragonfly
后面的命令将使用默认配置创建一个名为Dragonfly.rb的初始化程序。 我们暂时将其搁置一旁,但是您可以在Dragonfly的官方网站上阅读各种选择。
下一件重要的事情是为我们的模型配备Dragonfly方法。 这是通过使用dragonfly_accessor
完成的:
型号/album.rb
dragonfly_accessor :image
请注意,这里我说的是:image
它直接与我们在上一节中创建的image_uid
列有关。 如果,例如,一个名为您的专栏photo_uid
,那么dragonfly_accessor
方法将需要接受:photo
作为参数。
如果您使用的是Rails 4或5,则另一个重要步骤是在控制器允许的范围内标记:image
字段(不是:image_uid
!):
albums_controller.rb
params.require(:album).permit(:title, :singer, :image)
差不多了,我们已经准备好创建视图并开始上传文件!
创建视图
从索引视图开始:
views / albums / index.html.erb
<h1>Albums</h1><%= link_to 'Add', new_album_path %><ul><%= render @albums %>
</ul>
现在部分:
views / albums / _album.html.erb
<li><%= image_tag(album.image.url, alt: album.title) if album.image_stored? %><%= link_to album.title, album_path(album) %> by<%= album.singer %>| <%= link_to 'Edit', edit_album_path(album) %>| <%= link_to 'Remove', album_path(album), method: :delete, data: {confirm: 'Are you sure?'} %>
</li>
这里有两种蜻蜓方法要注意:
album.image.url
返回图像的路径。album.image_stored?
说明记录中是否有上传的文件。
现在添加新页面和编辑页面:
views / albums / new.html.erb
<h1>Add album</h1><%= render 'form' %>
views / albums / edit.html.erb
<h1>Edit <%= @album.title %></h1><%= render 'form' %>
views / albums / _form.html.erb
<%= form_for @album do |f| %><div><%= f.label :title %><%= f.text_field :title %></div><div><%= f.label :singer %><%= f.text_field :singer %></div><div><%= f.label :image %><%= f.file_field :image %></div><%= f.submit %>
<% end %>
格式没什么花哨的,但是再次注意,在渲染文件输入时,我们说的是:image
,不是:image_uid
。
现在您可以启动服务器并测试上传功能!
移除影像
因此,用户能够创建和编辑相册,但是存在一个问题:他们无法删除图像,只能用另一张图像替换。 幸运的是,这很容易通过引入“删除图像”复选框来解决:
views / albums / _form.html.erb
<% if @album.image_thumb_stored? %><%= image_tag(@album.image.url, alt: @album.title) %><%= f.label :remove_image %><%= f.check_box :remove_image %>
<% end %>
如果相册中有关联的图像,我们将其显示并渲染一个复选框。 如果选中此复选框,则图像将被删除。 请注意,如果您的字段名为photo_uid
,则删除附件的相应方法将是remove_photo
。 很简单,不是吗?
唯一要做的另一件事是允许控制器中的remove_image
属性:
albums_controller.rb
params.require(:album).permit(:title, :singer, :image, :remove_image)
添加验证
在这个阶段,一切工作都很好,但是我们根本没有检查用户的输入,这并不是特别好。 因此,让我们为专辑模型添加验证:
型号/album.rb
validates :title, presence: true
validates :singer, presence: true
validates :image, presence: true
validates_property :width, of: :image, in: (0..900)
validates_property
是Dragonfly方法,可以检查附件的各个方面:您可以验证文件的扩展名,MIME类型,大小等。
现在,让我们创建一个通用部分以呈现发现的错误:
views / shared / _errors.html.erb
<% if object.errors.any? %><div><h4>The following errors were found:</h4><ul><% object.errors.full_messages.each do |msg| %><li><%= msg %></li><% end %></ul></div>
<% end %>
在表单中使用此部分内容:
views / albums / _form.html.erb
<%= form_for @album do |f| %><%= render 'shared/errors', object: @album %><%# ... %>
<% end %>
样式化带有错误的字段以直观地描述它们:
样式表/application.scss
.field_with_errors {display: inline;label {color: red;}input {background-color: lightpink;}
}
在请求之间保留图像
引入验证后,我们遇到了另一个问题(很典型的情况,是吗?):如果用户在填写表单时犯了错误,则他或她将需要在单击“ 提交”按钮后再次选择文件。
蜻蜓还可以通过使用retained_*
隐藏字段来帮助您解决此问题:
views / albums / _form.html.erb
<%= f.hidden_field :retained_image %>
别忘了也允许该字段:
albums_controller.rb
params.require(:album).permit(:title, :singer, :image, :remove_image, :retained_image)
现在,该图像将在请求之间保持不变! 但是,唯一的小问题是文件上传输入仍将显示“选择文件”消息,但是可以通过一些样式和少量JavaScript来解决。
处理图像
产生缩图
我们的用户上传的图像可能具有非常不同的尺寸,这可能(并且可能会)对网站的设计造成负面影响。 您可能希望将图像缩小到某些固定尺寸,当然可以通过使用width
和height
样式来实现。 但是,这不是最佳方法:浏览器仍然需要下载完整大小的图像,然后将其缩小。
另一个选择(通常更好)是在服务器上生成具有某些预定义尺寸的图像缩略图。 使用Dragonfly确实很简单:
views / albums / _album.html.erb
<li><%= image_tag(album.image.thumb('250x250#').url, alt: album.title) if album.image_stored? %><%# ... %>
</li>
250x250
当然是尺寸,而#
是表示“如果需要以中心重心保持长宽比,则调整大小和裁剪”的几何形状。 您可以在Dragonfly网站上找到有关其他几何形状的信息 。
thumb
方法由ImageMagick支持,这是创建和处理图像的绝佳解决方案。 因此,为了在本地查看有效的演示,您需要安装ImageMagick (支持所有主要平台)。
默认情况下,Dragonfly的初始化程序中启用了对ImageMagick的支持:
config / initializers / dragonfly.rb
plugin :imagemagick
现在正在生成缩略图,但是它们不会存储在任何地方。 这意味着用户每次访问相册页面时,都会重新生成缩略图。 有两种方法可以解决此问题:通过在保存记录后生成它们,或通过即时生成。
第一个选项涉及引入新列以存储缩略图并调整dragonfly_accessor
方法。 创建并应用新的迁移:
rails g migration add_image_thumb_uid_to_albums image_thumb_uid:string
rails db:migrate
现在修改模型:
型号/album.rb
dragonfly_accessor :image docopy_to(:image_thumb){|a| a.thumb('250x250#') }
enddragonfly_accessor :image_thumb
需要注意的是,现在第一个电话dragonfly_accessor
发送实际生成的缩略图,我们和它复制到块image_thumb
。 现在,只需在视图中使用image_thumb
方法即可:
views / albums / _album.html.erb
<%= image_tag(album.image_thumb.url, alt: album.title) if album.image_thumb_stored? %>
该解决方案是最简单的,但是官方文档不建议这样做,更糟糕的是,在编写本文时,该解决方案不适用于retained_*
字段。
因此,让我向您展示另一个选择:动态生成缩略图。 它涉及创建一个新模型并调整Dragonfly的配置文件。 一,模型:
rails g model Thumb uid:string job:string
rake db:migrate
将thumbs
表将承载您的缩略图,但他们会来按需生成。 为此,我们需要在Dragonfly初始化程序中重新定义url
方法:
config / initializers / dragonfly.rb
Dragonfly.app.configure dodefine_url do |app, job, opts|thumb = Thumb.find_by_job(job.signature)if thumbapp.datastore.url_for(thumb.uid, :scheme => 'https')elseapp.server.url_for(job)endendbefore_serve do |job, env|uid = job.storeThumb.create!(:uid => uid,:job => job.signature)end# ...
end
现在添加新相册并访问根页面。 首次执行此操作时,以下输出将被打印到日志中:
DRAGONFLY: shell command: "convert" "some_path/public/system/dragonfly/development/2017/02/08/3z5p5nvbmx_Folder.jpg" "-resize" "250x250^^" "-gravity" "Center" "-crop" "250x250+0+0" "+repage" "some_path/20170208-1692-1xrqzc9.jpg"
这实际上意味着ImageMagick正在为我们生成缩略图。 但是,如果您重新加载页面,则此行将不再显示,这意味着缩略图已被缓存! 您可以在Dragonfly网站上有关此功能的信息 。
更多处理
上传图像后,您几乎可以对其进行任何处理。 这可以在after_assign
回调内部完成。 例如,让我们将所有图像转换为90%质量的JPEG格式:
dragonfly_accessor :image doafter_assign {|a| a.encode!('jpg', '-quality 90') }
end
您还可以执行更多操作:旋转和裁剪图像,使用不同的格式编码,在图像上写文本,与其他图像混合(例如,放置水印)等。要查看其他示例,请参阅蜻蜓网站上的ImageMagick部分 。
上载和管理歌曲
当然,我们音乐网站的主要部分是歌曲,所以现在就添加它们。 每首歌都有一个标题和一个音乐文件,并且属于一张专辑:
rails g model Song album:belongs_to title:string track_uid:string
rails db:migrate
像在Album
模型中一样,连接Dragonfly方法:
型号/song.rb
dragonfly_accessor :track
不要忘记建立has_many
关系:
型号/album.rb
has_many :songs, dependent: :destroy
添加新路线。 专辑范围内始终存在一首歌,因此我将嵌套以下路线:
config / routes.rb
resources :albums doresources :songs, only: [:new, :create]
end
创建一个非常简单的控制器(再次,请不要忘记允许track
字段):
songs_controller.rb
class SongsController < ApplicationControllerdef new@album = Album.find(params[:album_id])@song = @album.songs.buildenddef create@album = Album.find(params[:album_id])@song = @album.songs.build(song_params)if @song.saveflash[:success] = "Song added!"redirect_to album_path(@album)elserender :newendendprivatedef song_paramsparams.require(:song).permit(:title, :track)end
end
显示歌曲和添加新歌曲的链接:
views / albums / show.html.erb
<h1><%= @album.title %></h1>
<h2>by <%= @album.singer %></h2><%= link_to 'Add song', new_album_song_path(@album) %><ol><%= render @album.songs %>
</ol>
编写表格:
views / songs / new.html.erb
<h1>Add song to <%= @album.title %></h1><%= form_for [@album, @song] do |f| %><div><%= f.label :title %><%= f.text_field :title %></div><div><%= f.label :track %><%= f.file_field :track %></div><%= f.submit %>
<% end %>
最后,添加_song部分:
views / songs / _song.html.erb
<li><%= audio_tag song.track.url, controls: true %><%= song.title %>
</li>
我在这里使用的是HTML5 audio
标签,该标签不适用于较旧的浏览器。 因此,如果您打算支持此类浏览器,请使用polyfill 。
如您所见,整个过程非常简单。 蜻蜓并不真正在乎您要上传的文件类型。 您所需要做的就是提供一个dragonfly_accessor
方法,添加一个适当的字段,允许它,并呈现文件输入标签。
存储元数据
当我打开播放列表时,我希望看到有关每首歌曲的其他信息,例如持续时间或比特率。 当然,默认情况下,此信息不会存储在任何地方,但是我们可以很轻松地解决它。 Dragonfly允许我们提供有关每个上载文件的其他数据,并在以后使用meta
方法获取它。
但是,当我们处理音频或视频时,事情要复杂一些,因为要获取它们的元数据,需要特殊的gem streamio-ffmpeg 。 反过来,此gem依赖于FFmpeg ,因此,要继续进行,您需要将其安装在PC上。
将streamio-ffmpeg
添加到Gemfile中 :
宝石文件
gem 'streamio-ffmpeg'
安装它:
bundle install
现在,我们可以使用上after_assign
已经看到的相同的after_assign
回调:
型号/song.rb
dragonfly_accessor :track doafter_assign do |a|song = FFMPEG::Movie.new(a.path)mm, ss = song.duration.divmod(60).map {|n| n.to_i.to_s.rjust(2, '0')}a.meta['duration'] = "#{mm}:#{ss}"a.meta['bitrate'] = song.bitrate ? song.bitrate / 1000 : 0end
end
请注意,这里我使用的是path
方法,而不是url
,因为此时我们正在使用一个tempfile。 接下来,我们仅提取歌曲的持续时间(将其转换为以零开头的分钟和秒)和比特率(将其转换为每秒的千字节)。
最后,在视图中显示元数据:
views / songs / _song.html.erb
<li><%= audio_tag song.track.url, controls: true %><%= song.title %> (<%= song.track.meta['duration'] %>, <%= song.track.meta['bitrate'] %>Kb/s)
</li>
如果您检查public / system / dragonfly文件夹(托管上传的默认位置)上的内容,您会注意到一些.yml文件-它们以YAML格式存储所有元信息。
部署到Heroku
我们今天将讨论的最后一个主题是如何在部署到Heroku云平台之前准备应用程序。 主要问题是Heroku不允许您存储自定义文件(例如上载),因此我们必须依靠Amazon S3之类的云存储服务。 幸运的是,蜻蜓可以轻松地与其集成。
您需要做的就是在AWS上注册一个新帐户(如果您还没有),创建一个有权访问S3存储桶的用户,并在一个安全的位置写下该用户的密钥对。 您可以使用根密钥对,但是实际上不建议这样做 。 最后,创建一个S3存储桶。
回到我们的Rails应用程序,放入一个新的gem:
宝石文件
group :production dogem 'dragonfly-s3_data_store'
end
安装它:
bundle install
然后调整Dragonfly的配置以在生产环境中使用S3:
config / initializers / dragonfly.rb
if Rails.env.production?datastore :s3,bucket_name: ENV['S3_BUCKET'],access_key_id: ENV['S3_KEY'],secret_access_key: ENV['S3_SECRET'],region: ENV['S3_REGION'],url_scheme: 'https'
elsedatastore :file,root_path: Rails.root.join('public/system/dragonfly', Rails.env),server_root: Rails.root.join('public')
end
要在Heroku上提供ENV
变量,请使用以下命令:
heroku config:add SOME_KEY=SOME_VALUE
如果希望在本地测试与S3的集成,则可以使用dotenv-rails之类的gem管理环境变量。 但是请记住,您的AWS密钥对一定不能公开公开 !
我在部署到Heroku时遇到的另一个小问题是缺少FFmpeg。 问题在于,当创建一个新的Heroku应用程序时,它具有一组常用的服务(例如,默认情况下ImageImageick可用)。 其他服务可以作为Heroku插件或以buildpacks的形式安装。 要添加FFmpeg buildpack,请运行以下命令:
heroku buildpacks:add https://github.com/HYPERHYPER/heroku-buildpack-ffmpeg.git
现在一切就绪,您可以与世界分享您的音乐应用!
结论
这是一段漫长的旅程,不是吗? 今天,我们讨论了蜻蜓-一种在Rails中上传文件的解决方案。 我们已经看到了它的基本设置,一些配置选项,缩略图生成,处理和元数据存储。 此外,我们已经将Dragonfly与Amazon S3服务集成在一起,并准备了我们的应用程序以在生产中进行部署。
当然,本文没有讨论Dragonfly的所有方面,因此请确保浏览其官方网站以找到大量的文档和有用的示例。 如果您还有其他问题或遇到一些代码示例,请随时与我联系。
感谢您与我在一起,很快再见!
翻译自: https://code.tutsplus.com/tutorials/uploading-files-with-rails-and-dragonfly--cms-28184
使用Rails和Dragonfly上传文件相关推荐
- HTML上传文件的多种方式
1. 传统方式 <form id="upload-form" action="upload.php" method="post" en ...
- 如何使用 jQuery 异步上传文件?
问: 我想用 jQuery 异步上传文件. $(document).ready(function () { $("#uploadbutton").click(function () ...
- smartupload 上传文件时 把页面编码改成gbk 解决乱码
快来java1234 吧 smartupload 上传文件时,经常会发生因为把表单设置为 enctype="multipart/form-data"而出现的中文乱码问题,本人头疼好 ...
- 关于上传文件的跨域问题
在进行新框架开发的过程中,需要自定义页面组件实现脱离表单的文件(图片)上传,考虑过wex5自带的attachmentsimple的自定义写法很难受,就改用了第三方插件webuploader来实现选择文 ...
- html web上传文件原理,Web上传文件的原理及实现
本文为原创,如需转载,请注明作者和出处,谢谢! 现在有很多Web程序都有上传功能,实现上传功能的组件或框架也很多,如基于java的Commons FileUpload.还有Struts1.x和Stru ...
- php利用上传文件,如何利用PHP上传文件
上载文件表单 请选择文件: $upload_file=$_FILES['upload_file']['tmp_name']; $upload_file_name=$_FILES['upload_fil ...
- window linux上传文件命令,windows通过cmd命令行使用sftp上传文件至linux
一问:sftp是什么? sftp 是一个交互式文件传输程式.它类似于 ftp, 但它进行加密传输,比FTP有更高的安全性.下边就简单介绍一下如何远程连接主机,进行文件的上传和下载,以及一些相关操作. ...
- 怎么接收layui上传的文件_layui 上传文件_批量导入数据UI的方法
使用layui的文件上传组件,可以方便的弹出文件上传界面. 效果如下: 点击[批量导入]按钮调用js脚本importData(config)就可以实现数据上传到服务器. 脚本: /*** * 批量导入 ...
- php post 文件,PHP响应post请求上传文件的方法_php技巧
本文实例讲述了PHP响应post请求上传文件的方法.分享给大家供大家参考,具体如下: function send_file($url, $post = '', $file = '') { $eol = ...
最新文章
- 数据结构 互换二叉树中所有结点的左右子树 C
- UGUI 自动布局的重叠BUG
- 神秘的“阿里星”是一群怎么样的人
- curator分布式锁的基本使用
- 3000类别,20万个标注,山师等推出大规模Logo检测数据集:LogoDet-3K
- HDU-1863-畅通工程(并查集)
- oracle的表空间的检查,oracle数据库检查所有表空间使用率的脚本
- ZJOI2019 线段树
- 二叉搜索树与双向链表的转换
- ubuntu18.04播放MP4
- 开源好用的 Android 市场 F-Droid
- 计算机编程语言vf,2016年计算机二级VF语言程序设计考试大纲
- 英剧推荐【IT狂人】
- xm-select使用
- 一篇关于不同进制之间的转换、比如二进制、八进制、十进制、十六进制等
- Windows Server 2008密码破解
- MarGo: Missing required environment variables: GOROOT GOPATH See the `Quirks` section of USAGE.md fo
- 搜狗输入法乱码 解决
- 在ASP.NET 中检测手机浏览器(转)
- 平面最近点距离问题(分治法)