はじめに
写真を大量にアップロードするWebサービスを作っていると、アップロードされた写真をまとめてダウンロードさせたくなることがあります。
そこで、zipline + carrierwave を使うと非常に簡単に写真をZIPにまとめてダウンロードすることができたのでご紹介します。
今回の環境
Ruby on Rails 5.0
carrierwave (1.0.0)
fog (1.38.0)
zipline (0.0.12)
今回利用したgemはこちら
fringd/zipline: A gem that lets you stream a zip file from rails
https://github.com/fringd/zipline
前提条件
写真のアップロードにはcarrierwaveを使っています。
またアップロードされた写真はfogを使ってS3に保存しています。
この形式はRailsではよく使われる方法なので実装方法は割愛します。
ルーティング
本の画像をZIPで一括ダウンロードする様にしたいので、ルーティングは下記の様にします。
Rails.application.routes.draw do
resources :books, only: %i(index)
end
これで /books
でアクセスすると本の一覧を表示して、 /books.zip
でアクセスすると本の画像をZIPでダウンロードできるようにします。
実装方法
まずはgemを追加して bundle install
します。
gem 'carrierwave'
gem 'fog'
gem 'zipline'
本の一覧を表示するコードは今回の内容とは関係ないので省略しますが、ZIPで一括ダウンロードするリンクは下記の様にします。
= link_to 'ZIPでダウンロード', books_path(format: :zip)
Bookモデルにはimageという画像を保存するカラムを追加します。
class Book < ApplicationRecord
mount_uploader :image, ImageUploader
end
後はコントローラーにZIPでダウンロードするコードを記載します。
class BooksController < ApplicationController
include ActionController::Streaming
include Zipline
def index
@books = Book.all
respond_to do |format|
format.html
format.zip do
files = @books.where.not(image: nil).map{ |book| [book.image, "#{book.id}.jpg"] }
zipline(files, 'books.zip')
end
end
end
end
files
変数にZIPにまとめたい画像ファイルを配列で入れています。
入っている配列は下記の様になっています。
ziplineがcarrierwaveに対応しているのでcarrierwaveのカラムを直接渡してあげるだけでremote_urlが画像のパスとしてセットされます。
[
['https://example.s3-ap-northeast-1.amazonaws.com/uploads/book/image/1/image.jpg', '1.jpg']
['https://example.s3-ap-northeast-1.amazonaws.com/uploads/book/image/2/image.jpg', '2.jpg']
['https://example.s3-ap-northeast-1.amazonaws.com/uploads/book/image/3/image.jpg', '3.jpg']
]
注意点
コントローラーの中で @books.where.not(image: nil)
としてimageが無いbookは渡さない様にしています。
imageが空のカラムを渡してもエラーにはならずZIPダウンロードまで行えますが、ダウンロードしたZIPを解凍しようとすると不正なファイルとして解凍できないので注意が必要です。
まとめ
zipline + carrierwaveでとてもシンプルに一括ダウンロードが実装できました。
また、ziplineは Streaming
でダウンロードしているので大きなファイルサイズのZIPであっても快適にダウンロードが行えるようになっています。