不久前,我写了一篇文章“使用Rails和Shrine上传文件 ”,其中介绍了如何借助Shrine gem将文件上传功能引入Rails应用程序。 但是,有很多类似的解决方案可用,而我最喜欢的之一是Dragonfly ,这是Mark Evans为Rails和Rack创建的易于使用的上传解决方案。

去年年初 ,我们讨论了这个库,但与大多数软件一样,它有助于不时查看库,以了解更改的内容以及如何在应用程序中使用它。

在本文中,我将指导您完成蜻蜓的设置并解释如何利用其主要功能。 你将学到如何:

  • 将Dragonfly集成到您的应用程序中
  • 配置模型以与Dragonfly一起使用
  • 介绍基本的上传机制
  • 介绍验证
  • 生成图像缩略图
  • 执行文件处理
  • 存储上载文件的元数据
  • 准备要部署的应用程序

为了使事情变得更有趣,我们将创建一个小型音乐应用程序。 它将展示可以在网站上管理和播放的专辑和相关歌曲。

GitHub上提供了本文的源代码。 您还可以签出该应用程序的工作演示 。

列出和管理相册

首先,创建一个没有默认测试套件的新Rails应用程序:

rails new UploadingWithDragonfly -T

对于本文,我将使用Rails 5,但是大多数描述的概念也适用于旧版本。

创建模型,控制器和路径

我们的小型音乐网站将包含两个模型: AlbumSong 。 现在,让我们用以下字段创建第一个:

  • titlestring )-包含相册的标题
  • singerstring )—专辑的表演者
  • image_uidstring )—用于存储相册的预览图像的特殊字段。 您可以根据自己的喜好命名此字段,但必须按照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来解决。

处理图像

产生缩图

我们的用户上传的图像可能具有非常不同的尺寸,这可能(并且可能会)对网站的设计造成负面影响。 您可能希望将图像缩小到某些固定尺寸,当然可以通过使用widthheight样式来实现。 但是,这不是最佳方法:浏览器仍然需要下载完整大小的图像,然后将其缩小。

另一个选择(通常更好)是在服务器上生成具有某些预定义尺寸的图像缩略图。 使用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上传文件相关推荐

  1. HTML上传文件的多种方式

    1. 传统方式 <form id="upload-form" action="upload.php" method="post" en ...

  2. 如何使用 jQuery 异步上传文件?

    问: 我想用 jQuery 异步上传文件. $(document).ready(function () { $("#uploadbutton").click(function () ...

  3. smartupload 上传文件时 把页面编码改成gbk 解决乱码

    快来java1234 吧 smartupload 上传文件时,经常会发生因为把表单设置为 enctype="multipart/form-data"而出现的中文乱码问题,本人头疼好 ...

  4. 关于上传文件的跨域问题

    在进行新框架开发的过程中,需要自定义页面组件实现脱离表单的文件(图片)上传,考虑过wex5自带的attachmentsimple的自定义写法很难受,就改用了第三方插件webuploader来实现选择文 ...

  5. html web上传文件原理,Web上传文件的原理及实现

    本文为原创,如需转载,请注明作者和出处,谢谢! 现在有很多Web程序都有上传功能,实现上传功能的组件或框架也很多,如基于java的Commons FileUpload.还有Struts1.x和Stru ...

  6. php利用上传文件,如何利用PHP上传文件

    上载文件表单 请选择文件: $upload_file=$_FILES['upload_file']['tmp_name']; $upload_file_name=$_FILES['upload_fil ...

  7. window linux上传文件命令,windows通过cmd命令行使用sftp上传文件至linux

    一问:sftp是什么? sftp 是一个交互式文件传输程式.它类似于 ftp, 但它进行加密传输,比FTP有更高的安全性.下边就简单介绍一下如何远程连接主机,进行文件的上传和下载,以及一些相关操作. ...

  8. 怎么接收layui上传的文件_layui 上传文件_批量导入数据UI的方法

    使用layui的文件上传组件,可以方便的弹出文件上传界面. 效果如下: 点击[批量导入]按钮调用js脚本importData(config)就可以实现数据上传到服务器. 脚本: /*** * 批量导入 ...

  9. php post 文件,PHP响应post请求上传文件的方法_php技巧

    本文实例讲述了PHP响应post请求上传文件的方法.分享给大家供大家参考,具体如下: function send_file($url, $post = '', $file = '') { $eol = ...

最新文章

  1. 数据结构 互换二叉树中所有结点的左右子树 C
  2. UGUI 自动布局的重叠BUG
  3. 神秘的“阿里星”是一群怎么样的人
  4. curator分布式锁的基本使用
  5. 3000类别,20万个标注,山师等推出大规模Logo检测数据集:LogoDet-3K
  6. HDU-1863-畅通工程(并查集)
  7. oracle的表空间的检查,oracle数据库检查所有表空间使用率的脚本
  8. ZJOI2019 线段树
  9. 二叉搜索树与双向链表的转换
  10. ubuntu18.04播放MP4
  11. 开源好用的 Android 市场 F-Droid
  12. 计算机编程语言vf,2016年计算机二级VF语言程序设计考试大纲
  13. 英剧推荐【IT狂人】
  14. xm-select使用
  15. 一篇关于不同进制之间的转换、比如二进制、八进制、十进制、十六进制等
  16. Windows Server 2008密码破解
  17. MarGo: Missing required environment variables: GOROOT GOPATH See the `Quirks` section of USAGE.md fo
  18. 搜狗输入法乱码 解决
  19. 在ASP.NET 中检测手机浏览器(转)
  20. 平面最近点距离问题(分治法)

热门文章

  1. 做嵌入式开发有35岁瓶颈期嘛?
  2. 阿里主动改革,再次引领国内公司治理新浪潮
  3. 用Java编写求出100~999之间的水仙花数
  4. C++11移动构造函数详解
  5. 进程地址空间(虚拟地址 | 物理内存)
  6. Linux进程通信:无名管道
  7. linux添加有效群组,linux 用户/群组/权限
  8. 阿里巴巴矢量图的使用
  9. 微信扫码支付成功后跳转
  10. 等高布局、圣杯布局、双飞翼布局