使用Rails和Heroku或AWS实现图片上传和缩略图创建(实践部分)

首先

我在一年前写了一篇关于使用Rails、Heroku和AWS进行令人兴奋的图片上传并生成缩略图的文章。

在那之后的大约一年时间里,我们不断改善和变化我们的策略,现在我将向大家介绍我们正在实际运用的做法。在这里,我将通过创建一个能够给图片添加评论的论坛来进行解释。

这种方法的好处

    • 画像アップロードが早い

画像をブラウザ側でリサイズして軽量化
HerokuではなくS3へ直接アップロード
非同期

投稿が早い

画像を事前にアップロードするため、投稿時にはそのパスを渡すだけ
サムネイルはアクセスされた時に同期的に作成するため、投稿のレスポンスは即時

サムネイル表示が早い

作成したサムネイルはS3へ保存するため重複が発生しない
既にサムネイルを作成している場合は直接S3のURLを返すため、Herokuへのアクセスが発生しない
サムネイルとS3パスの対応はmemcacheでキャッシュしているため引くのが早い
S3の画像はCloudFrontにキャッシュするので早い

机制概述

ここではこの仕組みの概要を説明します。心ではなく身体で学びたい方は、この節は飛ばしてしまって構いません。

环境建设方法

    1. AWS S3のUS Standard リージョンに画像 (オリジナル, サムネイル) 保存用バケットを作成

 

    1. 上記バケットに対応したCloudFront ディストリビューションを作成

 

    1. Dragonfly gem, Dragonfly::S3DataStore gem を導入

 

    1. 画像を持たせるモデルのテーブルに画像のパスを保存するカラムを追加

 

    1. 上記モデルにDragonflyアクセサの設定を追加

 

    S3へのアクセスはCloudFrontを通すようDragonflyを設定

图片上传过程

    1. 用户在图片选择对话框中选择图片时,立即异步执行以下操作:

对图片进行轻量化处理(保持宽高比的同时缩小宽度)

以缩短上传时间为目的

直接将图片上传到S3(亚马逊对象存储服务)

由于通过Heroku上传大容量文件存在许多冗余,因此选择直接上传到S3

将上传后的URL插入到表单中

用户提交表单后,服务器端执行以下操作:

解析提交的上传URL,提取出S3上的路径

将提取的路径插入到模型中已存在的Dragonfly列中

画像表示的过程

    1. 在View中,通过使用Dragonfly来指定图像格式并生成缩略图的URL。

 

    1. (事先将缩略图图像的路径和生成缩略图的信息哈希对保存到DB和memcached中)如果已经存在指定格式的缩略图图像,则返回在S3中创建的缩略图图像的URL。

 

    1. 同时,返回通过CloudFront传递的URL。

 

    1. 如果不存在,则生成带有缩略图生成信息的Heroku URL。

 

    1. 当用户使用上述URL在img标签中显示时,同步执行以下操作:

 

    1. 从S3中加载原始图像,在Heroku上创建缩略图图像

 

    1. 将上述缩略图图像返回给浏览器

 

    1. 将生成的缩略图图像保存到S3

 

    将缩略图生成信息的哈希和缩略图图像的路径组合保存到DB中。

创建测试应用的示例

咚咚咚(颤抖声)。命令输出将被省略。

anoworl@crow ~ $ rails -v
Rails 4.1.8
anoworl@crow ~ $ rails new ImageBoard
anoworl@crow ~ $ cd ImageBoard
anoworl@crow ~/ImageBoard $ vi Gemfile
gem 'dragonfly'
gem 'dragonfly-s3_data_store'
gem 's3_file_field'
gem 'dotenv-rails', group: :development
anoworl@crow ~/ImageBoard $ bundle install
anoworl@crow ~/ImageBoard $ bundle exec rails g scaffold post title body:text image_uid
anoworl@crow ~/ImageBoard $ bundle exec rake db:create
anoworl@crow ~/ImageBoard $ bundle exec rake db:migrate
anoworl@crow ~/ImageBoard $ bundle exec rails s

在AWS S3上创建一个用于开发的存储桶。区域设定为Heroku的默认区域,即美国标准区(US Standard)。

バケット作成

为了进行JavaScript直接上传,需要进行CORS(跨域资源共享)设置。

S3 Management Console

输入以下内容并保存。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>POST</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

创建并设置配置文件(将方括号内的值替换为实际值!)

S3_BUCKET=[Bucket Name]
S3_KEY=[Access Key ID]
S3_SECRET=[Secret Access Key]
anoworl@crow ~/ImageBoard $ rails g dragonfly

将数据存储从文件更改为S3。

  datastore :s3,
    bucket_name: ENV['S3_BUCKET'],
    access_key_id: ENV['S3_KEY'],
    secret_access_key: ENV['S3_SECRET'],
    url_host: ENV['S3_CDN_HOST']

还要设置用于直接上传的 s3_file_field gem。

S3FileField.config do |c|
  c.access_key_id = ENV['S3_KEY']
  c.secret_access_key = ENV['S3_SECRET']
  c.bucket = ENV['S3_BUCKET']
end

设置模型。

class Post < ActiveRecord::Base
  dragonfly_accessor :image
end

在中文中设定前端。

//= require s3_file_field
//= require_tree .
    <%= f.s3_file_field :image_uid %>
$(document).on 'ready page:load', ->
  $this = -> $("#post_image_uid")
  $this().S3FileField
    done: (e, data) ->
      $this().attr(type: 'text', value: data.result.filepath.slice(1), readonly: true)
        <td><%= image_tag post.image.thumb('100x100#').url %></td>

暂时先行动起来。

スクリーンショット 2014-12-03 18.22.12.png

选择文件后,路径会显示。

スクリーンショット 2014-12-03 18.23.28.png

一応動く。

スクリーンショット 2014-12-03 18.48.00.png

宿題

由于重新实施起来意外地相当耗时,所以剩下的部分将在以后陆续添加进去。

    • ファイル名対応

URLエンコードされたパスのデコード
パスをS3に合わせてNFCへ正規化

ブラウザでの画像軽量化
生成したサムネイルの保存

S3への保存
サムネイルのパスをmemcachedにキャッシュ

CloudFront対応
Herokuへのデプロイ

最后

未来的展望

    • CloudFront経由で画像をアップロード

早くなる可能性を検証したい

2014 年的 Heroku 圣诞日历

明天还有很多空位!明天怎么办呢……。

广告
将在 10 秒后关闭
bannerAds