当前位置: 首页 > 工具软件 > carrierwave > 使用案例 >

用Rails和Carrierwave上传

狄宜然
2023-12-01

这是“使用Rails上传”系列中的另一篇文章。 今天,我们将认识Carrierwave ,这是Rails最受欢迎的文件上传解决方案之一。 我喜欢Carrierwave,因为它很容易上手,具有很多现成的功能,并且提供了社区成员撰写的数十篇“如何”文章,因此您不会迷路。

在本文中,您将学习如何:

  • 将Carrierwave集成到您的Rails应用中
  • 添加验证
  • 跨请求保留文件
  • 删除档案
  • 产生缩图
  • 从远程位置上传文件
  • 介绍多个文件上传
  • 添加对云存储的支持

GitHub上提供了本文的源代码。 享受阅读!

奠定基础

与往常一样,首先创建一个新的Rails应用程序:

rails new UploadingWithCarrierwave -T

对于此演示,我将使用Rails 5.0.2。 请注意,Carrierwave 1仅支持Rails 4+和Ruby2。如果仍在使用Rails 3,请连接Carrierwave版本0.11。

为了了解Carrierwave的实际应用,我们将创建一个具有唯一Post模型的非常简单的博客应用程序。 它将具有以下主要属性:

  • titlestring
  • bodytext
  • imagestring )-此字段将包含附加到帖子的图像(准确地说是文件名)

生成并应用新的迁移:

rails g model Post title:string body:text image:string
rails db:migrate

设置一些路线:

config / routes.rb

resources :posts
root to: 'posts#index'

另外,创建一个非常基本的控制器:

posts_controller.rb

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update]

  def index
    @posts = Post.order('created_at DESC')
  end

  def show
  end

  def new
    @post = Post.new
  end

  def create
    @post = Post.new(post_params)
    if @post.save
      redirect_to posts_path
    else
      render :new
    end
  end

  def edit
  end

  def update
    if @post.update_attributes(post_params)
      redirect_to post_path(@post)
    else
      render :edit
    end
  end

  private

  def post_params
    params.require(:post).permit(:title, :body, :image)
  end

  def set_post
    @post = Post.find(params[:id])
  end
end

现在让我们制作索引视图:

views / posts / index.html.erb

<h1>Posts</h1>

<%= link_to 'Add post', new_post_path %>

<%= render @posts %>

并对应部分:

views / posts / _post.html.erb

<h2><%= link_to post.title, post_path(post) %></h2>

<p><%= truncate(post.body, length: 150) %></p>

<p><%= link_to 'Edit', edit_post_path(post) %></p>
<hr>

在这里,我使用Rails truncate方法仅显示帖子中的前150个符号。 在创建其他视图和部分表单之前,让我们首先将Carrierwave集成到应用程序中。

整合载波

将新的gem放入Gemfile中

宝石文件

gem 'carrierwave', '~> 1.0'

跑:

bundle install

Carrierwave将其配置存储在模型中包含的上载器中。 要生成上传器,请使用以下命令:

rails generate uploader Image

现在,在app / uploaders内部,您将找到一个名为image_uploader.rb的新文件。 请注意,它具有一些有用的注释和示例,因此您可以使用它开始。 在此演示中,我们将使用ActiveRecord,但Carrierwave 还支持 Mongoid,Sequel和DataMapper。

接下来,我们需要将此上传器包含或安装到模型中:

模型/ post.rb

mount_uploader :image, ImageUploader

上载器已经具有合理的默认设置,但是至少我们需要选择上载文件的存储位置。 现在,让我们使用文件存储:

uploaders / image_uploader.rb

storage :file

默认情况下,文件将放置在public / uploads目录中,因此最好将其从版本控制系统中排除:

.gitignore

public/uploads

您也可以在上传器内部修改store_dir方法,以选择其他位置。

此时,我们可以创建一个新视图和一个局部表单以开始上传文件:

views / posts / new.html.erb

<h1>Add post</h1>

<%= render 'form', post: @post %>

views / posts / _form.html.erb

<%= form_for post do |f| %>
  <div>
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>

  <div>
    <%= f.label :body %>
    <%= f.text_area :body %>
  </div>

  <div>
    <%= f.label :image %>
    <%= f.file_field :image %>
  </div>

  <%= f.submit %>
<% end %>

请注意,由于我们已经允许使用image属性,因此无需修改PostsController

最后,创建编辑视图:

views / posts / edit.html.erb

<h1>Edit post</h1>

<%= render 'form', post: @post %>

而已! 您可以引导服务器并尝试创建带有图像的帖子。 问题在于该图像在任何地方都不可见,因此让我们继续下一部分并添加显示页面!

显示影像

因此,我们尚未创建的唯一视图是show 。 立即添加:

views / posts / show.html.erb

<%= link_to 'All posts', posts_path %>
<h1><%= @post.title %></h1>

<%= image_tag(@post.image.url, alt: 'Image') if @post.image? %>

<p><%= @post.body %></p>

<p><%= link_to 'Edit', edit_post_path(@post) %></p>

如您所见,显示附件确实很容易:您所要做的就是说@post.image.url来获取图像的URL。 要获取文件的路径,请使用current_path方法。 请注意,载波还提供image? 我们检查附件是否存在的方法(即使文件不存在, image方法本身也不会返回nil )。

现在,导航到帖子后,您应该会看到一张图片,但是它可能看起来太大了:毕竟,我们没有在任何地方限制尺寸。 当然,我们可以使用一些CSS规则按比例缩小图像,但是在文件上传后生成缩略图要好得多。 但是,这需要一些其他步骤。

产生缩图

为了裁剪和缩放图像,我们需要一个单独的工具。 开箱即用Carrierwave支持RMagickMiniMagick宝石,而宝石又可以在ImageMagick的帮助下操纵图像。 ImageMagick是一个开放源代码解决方案,允许您编辑现有图像并生成新图像,因此在继续之前,您需要下载并安装它 。 接下来,您可以自由选择两个宝石之一。 我会坚持使用MiniMagick,因为它更容易安装并且具有更好的支持:

宝石文件

gem 'mini_magick'

跑:

bundle install

然后将MiniMagick包含在您的上传器中:

uploaders / image_uploader.rb

include CarrierWave::MiniMagick

现在,我们只需要向上传器介绍一个新版本。 版本 (或样式)的概念在许多文件上传库中使用。 它只是意味着将基于原始附件创建其他文件,例如具有不同的尺寸或格式。 引入一个称为thumb的新版本:

uploaders / image_uploader.rb

version :thumb do
    process resize_to_fill: [350, 350]
end

您可以根据需要拥有任意多个版本,而且,甚至可以在其他版本之上构建版本:

uploaders / image_uploader.rb

version :small_thumb, from_version: :thumb do
    process resize_to_fill: [20, 20]
end

如果您已经上传了一些图像,则它们将没有缩略图。 不过,这不是问题,因为您可以从Rails控制台重新创建它们:

rails c
Post.find_each {|post| post.image.recreate_versions!(:thumb) if post.image?}

最后,显示带有原始图像链接的缩略图:

views / posts / show.html.erb

<%= link_to(image_tag(@post.image.thumb.url, alt: 'Image'), @post.image.url, target: '_blank') if @post.image? %>

引导服务器并观察结果!

添加验证

目前,我们的上传正常,但我们根本没有验证用户的输入,这当然是不好的。 只要我们只想处理图片,就将.png,.jpg和.gif扩展名列入白名单:

uploaders / image_uploader.rb

def extension_whitelist
    %w(jpg jpeg gif png)
end

您还可以通过定义content_type_whitelist方法来添加内容类型检查:

uploaders / image_uploader.rb

def content_type_whitelist
    /image\//
end

或者,可以通过定义content_type_blacklist方法将某些文件类型(例如可执行文件)列入黑名单。

除了检查文件的类型和扩展名之外,我们还强制它小于1兆字节。 为此,我们将需要一个额外的gem支持 ActiveModel的文件验证

宝石文件

gem 'file_validators'

安装它:

bundle install

现在介绍所需的验证(注意,我还将添加对titlebody属性的检查):

模型/ post.rb

validates :title, presence: true, length: {minimum: 2}
validates :body, presence: true
validates :image, file_size: { less_than: 1.megabytes }

接下来要做的是为Carrierwave的错误消息添加I18n转换:

config / locales / en.yml

en:
  errors:
    messages:
      carrierwave_processing_error: "Cannot resize image."
      carrierwave_integrity_error: "Not an image."
      carrierwave_download_error: "Couldn't download image."
      extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
      extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"

当前,我们不会在任何地方显示验证错误,因此让我们创建一个共享的部分:

views / shared / _errors.html.erb

<% if object.errors.any? %>
  <h3>Some errors were found:</h3>
  <ul>
    <% object.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

在表单中使用此部分内容:

views / posts / _form.html.erb

<%= render 'shared/errors', object: post %>

现在尝试上传一些无效文件并观察结果。 它应该可以工作,但是如果您选择一个有效的文件并且不填写标题或正文,则检查仍将失败并显示错误。 但是,文件字段将被清除,用户将需要再次选择图像,这不是很方便。 要解决此问题,我们需要在表单中添加另一个字段。

跨请求保留文件

在表单重新显示中持久保存文件实际上非常容易。 您需要做的就是添加一个新的隐藏字段,并将其允许在控制器内部:

views / shared / _form.html.erb

<%= f.label :image %>
<%= f.file_field :image %><br>
<%= f.hidden_field :image_cache %>

posts_controller.rb

params.require(:post).permit(:title, :body, :image, :image_cache)

现在, image_cache将自动填充,并且图像不会丢失。 同时显示缩略图可能会有所帮助,以便用户了解图像已成功处理:

views / shared / _form.html.erb

<% if post.image? %>
    <%= image_tag post.image.thumb.url %>
<% end %>

移除影像

另一个非常常见的功能是在编辑记录时删除附件的功能 。 使用Carrierwave,实现此功能不是问题。 在表单中添加一个新的复选框:

views / shared / _form.html.erb

<% if post.image? %>
    <%= image_tag post.image.thumb.url %>
    <div>
      <%= label_tag :remove_image do %>
        Remove image
        <%= f.check_box :remove_image %>
      <% end %>
    </div>
<% end %>

并允许remove_image属性:

posts_controller.rb

params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache)

而已! 要手动删除图像,请使用remove_image! 方法:

@post.remove_image!

从远程位置上传

Carrierwave还提供了一个非常酷的功能,即开箱即用:通过URL 从远程位置上传文件的功能。 现在,通过添加一个新字段并允许相应的属性来介绍此功能:

views / shared / _form.html.erb

<%= f.text_field :remote_image_url %>
<small>Enter URL to an image</small>

posts_controller.rb

params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url)

多么酷啊? 您根本不需要进行任何更改,并且可以立即测试此功能!

处理多个上传

假设我们希望我们的帖子有多个附件。 在当前设置下,这是不可能的,但幸运的是,Carrierwave也支持这种情况。 要实现此功能,您需要添加序列化字段(对于SQLite)或JSON字段(对于Postgres或MySQL)。 我更喜欢后者,所以现在我们切换到新的数据库适配器。 从Gemfile中删除sqlite3 gem,然后添加pg:

宝石文件

gem 'pg'

安装它:

bundle install

像这样修改数据库配置:

config / database.yml

default: &default
  adapter: postgresql
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: upload_carrier_dev
  username: 'YOUR_USER'
  password: 'YOUR_PASSWORD'
  host: localhost

创建相应的Postgres数据库,然后生成并应用迁移:

rails g migration add_attachments_to_posts attachments:json
rails db:migrate

如果您喜欢使用SQLite,请按照 Carrierwave文档中列出的说明进行操作。

现在安装上载器(注意复数形式!):

模型/ post.rb

mount_uploaders :attachments, ImageUploader

我为附件使用了相同的上传器,但是您当然可以使用不同的配置生成一个新的上传器。

将多个文件字段添加到您的窗体:

views / shared / _form.html.erb

<div>
    <%= f.label :attachments %>
    <%= f.file_field :attachments, multiple: true %>
</div>

只要attachments字段将包含一个数组,就应该通过以下方式允许它:

posts_controller.rb

params.require(:post).permit(:title, :body, :image, :remove_image, :image_cache, :remote_image_url, attachments: [])

最后,您可以遍历帖子的附件并像往常一样显示它们:

views / shared / show.html.erb

<% if @post.attachments? %>
  <ul>
    <% @post.attachments.each do |attachment| %>
      <li><%= link_to(image_tag(attachment.thumb.url, alt: 'Image'), attachment.url, target: '_blank') %></li>
    <% end %>
  </ul>
<% end %>

请注意,每个附件都将具有在ImageUploader配置的缩略图。 真好!

使用云存储

坚持文件存储并不总是很方便和/或可能的,例如在Heroku上不可能存储自定义文件。 因此,您可能会问如何将Carrierwave与Amazon S3云存储结合? 嗯,这也是一个非常容易的任务 。 载波依赖于fog-aws gem来实现此功能:

宝石文件

gem "fog-aws"

安装它:

bundle install

让我们为Carrierwave创建一个初始化程序并全局配置云存储:

config / initializers / carrierwave.rb

CarrierWave.configure do |config|
  config.fog_provider = 'fog/aws'
  config.fog_credentials = {
      provider:              'AWS',
      aws_access_key_id:     ENV['S3_KEY'],
      aws_secret_access_key: ENV['S3_SECRET'],
      region:                ENV['S3_REGION'],
  }
  config.fog_directory  = ENV['S3_BUCKET']
end

还有其他一些可用选项,可以在文档中找到。

我正在使用dotenv-rails gem以安全的方式设置环境变量,但是您可以选择任何其他选项。 但是,请确保您的S3密钥对不公开,否则任何人都可以将任何内容上传到您的存储桶!

接下来,将storage :file行替换为:

uploaders / image_uploader.rb

storage :fog

除S3外,Carrierwave还支持上传到Google StorageRackspace 。 这些服务也很容易设置。

结论

今天就是这样! 我们已经介绍了Carrierwave的所有主要功能,现在您可以在项目中开始使用它了。 它具有一些可用的其他选项,因此请浏览文档

如果您陷入困境,请随时发布您的问题。 此外,窥视Carrierwave的Wiki可能很有用,该Wiki包含有用的“如何”文章,回答了许多常见问题。

因此,感谢您与我在一起,并祝您编程愉快!

翻译自: https://code.tutsplus.com/articles/uploading-with-rails-and-carrierwave--cms-28409

 类似资料: