使用Rails和Heroku或AWS实现图片上传和缩略图创建(实践部分)
首先
我在一年前写了一篇关于使用Rails、Heroku和AWS进行令人兴奋的图片上传并生成缩略图的文章。
在那之后的大约一年时间里,我们不断改善和变化我们的策略,现在我将向大家介绍我们正在实际运用的做法。在这里,我将通过创建一个能够给图片添加评论的论坛来进行解释。
这种方法的好处
-
- 画像アップロードが早い
画像をブラウザ側でリサイズして軽量化
HerokuではなくS3へ直接アップロード
非同期
投稿が早い
画像を事前にアップロードするため、投稿時にはそのパスを渡すだけ
サムネイルはアクセスされた時に同期的に作成するため、投稿のレスポンスは即時
サムネイル表示が早い
作成したサムネイルはS3へ保存するため重複が発生しない
既にサムネイルを作成している場合は直接S3のURLを返すため、Herokuへのアクセスが発生しない
サムネイルとS3パスの対応はmemcacheでキャッシュしているため引くのが早い
S3の画像はCloudFrontにキャッシュするので早い
机制概述
ここではこの仕組みの概要を説明します。心ではなく身体で学びたい方は、この節は飛ばしてしまって構いません。
环境建设方法
-
- AWS S3のUS Standard リージョンに画像 (オリジナル, サムネイル) 保存用バケットを作成
-
- 上記バケットに対応したCloudFront ディストリビューションを作成
-
- Dragonfly gem, Dragonfly::S3DataStore gem を導入
-
- 画像を持たせるモデルのテーブルに画像のパスを保存するカラムを追加
-
- 上記モデルにDragonflyアクセサの設定を追加
- S3へのアクセスはCloudFrontを通すようDragonflyを設定
图片上传过程
-
- 用户在图片选择对话框中选择图片时,立即异步执行以下操作:
对图片进行轻量化处理(保持宽高比的同时缩小宽度)
以缩短上传时间为目的
直接将图片上传到S3(亚马逊对象存储服务)
由于通过Heroku上传大容量文件存在许多冗余,因此选择直接上传到S3
将上传后的URL插入到表单中
用户提交表单后,服务器端执行以下操作:
解析提交的上传URL,提取出S3上的路径
将提取的路径插入到模型中已存在的Dragonfly列中
画像表示的过程
-
- 在View中,通过使用Dragonfly来指定图像格式并生成缩略图的URL。
-
- (事先将缩略图图像的路径和生成缩略图的信息哈希对保存到DB和memcached中)如果已经存在指定格式的缩略图图像,则返回在S3中创建的缩略图图像的URL。
-
- 同时,返回通过CloudFront传递的URL。
-
- 如果不存在,则生成带有缩略图生成信息的Heroku URL。
-
- 当用户使用上述URL在img标签中显示时,同步执行以下操作:
-
- 从S3中加载原始图像,在Heroku上创建缩略图图像
-
- 将上述缩略图图像返回给浏览器
-
- 将生成的缩略图图像保存到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(跨域资源共享)设置。
输入以下内容并保存。
<?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>
暂时先行动起来。
选择文件后,路径会显示。
一応動く。
宿題
由于重新实施起来意外地相当耗时,所以剩下的部分将在以后陆续添加进去。
-
- ファイル名対応
URLエンコードされたパスのデコード
パスをS3に合わせてNFCへ正規化
ブラウザでの画像軽量化
生成したサムネイルの保存
S3への保存
サムネイルのパスをmemcachedにキャッシュ
CloudFront対応
Herokuへのデプロイ
最后
未来的展望
-
- CloudFront経由で画像をアップロード
早くなる可能性を検証したい
2014 年的 Heroku 圣诞日历
明天还有很多空位!明天怎么办呢……。