その他
    ホーム 技術発信 DoRuby S3から複数のファイルを取得しつつ on-the-fly でZipを作ってブラウザに返す
    S3から複数のファイルを取得しつつ on-the-fly でZipを作ってブラウザに返す
     

    S3から複数のファイルを取得しつつ on-the-fly でZipを作ってブラウザに返す

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    S3にダウンロードさせたいファイルが複数あるが、zipファイルでまとめて1度にダウンロードさせたいが、そんなAPIはそもそも無いので zipline という gem を使ってみました。

    課題

    S3にダウンロードさせたいファイルが複数あるが、zipファイルでまとめて1度にダウンロードさせたい

    解決策

    • EC2インスタンスで稼働しているWebアプリケーション側でS3上の複数のファイルをまとめてzipを作成
    • zipファイルを作成するそばからクライアントへレスポンスを返す(chancked response)
      • ちょうどそんなことをやってくれる zipline という便利な gem が存在する

    (タイトル画像のようなイメージ)

    テスト用アプリを作る

    試しに Rails アプリケーションを作ってみる

    環境

    $ sw_vers
    ProductName:    Mac OS X
    ProductVersion: 10.10.5
    BuildVersion:   14F2109
    
    $ ruby -v
    ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin14]
    
    $ rails --version
    Rails 5.0.1
    

    Railsアプリの準備

    まず、準備として rails コマンドでアプリの雛形を用意

    $ rails new zipline_test
    

    上記が完了したら作成されたディレクトリに移動

    $ cd zipline_test
    

    Gemfileを編集して ‘zipline’ と ‘aws-sdk’ を追加してそれらのgemをインストール

    $ echo "gem 'zipline'" >> Gemfile
    $ echo "gem 'aws-sdk'" >> Gemfile
    $ bundle install --path ./bundle
    

    これでほぼ準備OK
    アプリケーションを書いていく

    ルートとコントローラを作る

    まずURIを定義するため、config/routes.rb を以下のように編集。

    Rails.application.routes.draw do
      get "download/zip"
    end
    

    次に、上記で定義したURIに対応するコントローラを定義する。
    以下のコマンドでコントローラの雛形を作成。

    $ rails g controller download
    

    すると、app/controllers/download_controller.rb が作成されるので、そのファイルを以下のように編集。

    class DownloadController < ApplicationController
      include ActionController::Streaming
      include Zipline
    
      AWS_S3_REGION = "ap-northeast-1"
      AWS_S3_BUCKET = "zipline-test"
    
      def zip
        zipline(s3files, "s3files.zip")
      end
    
      private
    
      # ダウンロード対象のファイルを順次zipに固めながら chunked response としてクライアントに返す
      def s3files
        s3 = Aws::S3::Client.new(region: AWS_S3_REGION)
        # ダウンロード対象のファイル
        files = %w(
          file1.json
          file2.json
          file3.json
        )
        files.lazy.map do |file|
          logger.debug "get file from s3 : #{file}"
          s3_object = s3.get_object(bucket: AWS_S3_BUCKET, key: file)
          

    [s3_object.body, file]

    end end end

    • AWS_S3_REGION は自分のS3バケットを作成したリージョンに
    • AWS_S3_BUCKET は自分のS3バケットの名前に
    • file1.json, file2.json, file3.json はS3バケット上のダウンロードさせたいファイル名に

    サーバを起動する

    あらかじめAWSに接続するためのcredential関係の環境変数(AWS_SECRET_ACCESS_KEY,AWS_ACCESS_KEY_ID)を設定してからサーバを起動する

    $ export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxx
    $ export AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxx
    $ rails s
    

    これで http://localhost:3000/で サーバが立ち上がるはず。

    URIアクセスしてみる

    ブラウザで http://localhost:3000/download/zip にアクセスするとダウンロードが始まり、zipファイルがダウンロードされるはず。

    参考