ちょっと話題の記事

[Ruby on Rails]画像をアップロード・ダウンロードするコントローラの呼び出しとRSpec

2014.06.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

画像をアップロード・ダウンロードする処理をRuby on Railsで作成する際、画面がないなどの理由で、コンソールから画像をアップロードする事や、ControllerのRSpecにて動作を確認することがあります。
今回はそれらの具体的なソースと、簡単なRailsの実装について書いてみたいと思います。

1.今回作成するアプリについて

先に書いたように、画像をアップロード・ダウンロードする処理をRuby on Railsにて実装します。アップロードした画像はアプリサーバ内の/public/imgフォルダ内に保存します。ダウンロード時にはURLパラメータにてファイル名を指定し、/public/imgフォルダ内よりファイルを取得してクライアントに送る仕組みです。(ファイル名を直に指定するのは、サンプルなので簡略化するためです。)

2.ルーティング

アップロード処理はarticles/uploadをpostで呼び出し、ダウンロード処理はarticles/downloadをgetで呼び出すようにしました。これらを定義したroutes.rbは以下のようになります。

/config/routes.rb

Rails.application.routes.draw do
  post 'articles/upload'
  get 'articles/download'

3.アップロード

Rails

では、アップロード処理についてです。まずはRailsのソースについてです。

/app/controllers/articles_controller.rb

class ArticlesController < ApplicationController

  def upload
    file = params[:img]
    name = file.original_filename

    File.open("public/img/#{name}", 'wb') { |f|
      f.write(file.read)
    }

    render nothing: true, status: 200
  end
(以下略)
[/ruby]
<p>
アップロード処理は「upload」というアクション名で作成しました。4行目で「img」パラメータとしてPOSTされてきた画像を取得し、8行目で読み込んでいます。同時にローカルのファイルとして書き出すことで、ローカルフォルダ内に画像を保存しています。
</p>
<h3>コンソールからの画像のアップロード</h3>
<p>
上記のコントローラの動きを確認するため、実際に画像をアップロードするためには、curlを使用します。コンソールを開き、以下のコマンドを実行することで、上記のコントローラを呼び出すことができました。
</p>

<p>
curlコマンドの引数に「POST」を指定することで、文字通りPOST送信を行います。また「-F」を指定することで、送信するフィールドと値を指定します。値に「@」をつけることで、multipart/form-dataで送信されます。	
</p>
<h3>RSpec</h3>
<p>
上記のコントローラを呼び出すRSpecには、以下のようになります。
</p>
<h4>/spec/controllers/articles_controller_spec.rb</h4>

<p>
クラウアントに画像を送信するため、4行目で「send_data」メソッドを呼び出しています。引数には指定されたファイル名、ヘッダーのタイプ、送信するファイル名を指定しています。
</p>
<h3>RSpec</h3>
<p>
このコントローラのRSpecは以下の通りです。尚、ダウンロード処理はGETリクエストであるため、実際の操作確認はブラウザから行うことが出来ます。
</p>
<h4>/spec/controllers/articles_controller_spec.rb</h4>

  describe "GET 'download'" do
    before(:all) do
      FileUtils.copy(Rails.root.to_s + '/spec/fixtures/' + img_file_path, Rails.root.to_s + '/' + upload_file_path) if !File.exist?(upload_file_path)
    end

    it "succeed file download." do
      get 'download', filename: IMG_FILE_NAME
      expect(response).to be_success
      expect(response.headers["Content-Disposition"]).to eq("attachment; filename=\"#{IMG_FILE_NAME}\"")
      expect(response.content_type).to eq("application/octet-stream")
    end
  end

何をもってファイルがダウンロードされるのかをテストするのかは難しい問題である部分もありますが、今回は送信されるレスポンスの種類、ファイル名を9、10行目でテストしました。

まとめ

普段はブラウザから送信する画像を指定して動作を確認するなどしていましたが、APIを作成する場合など、画面がないケースもあるかと思います。APIとして公開するインターフェースにもよるかと思いますが、ブラウザを使用しないで画像のアップロード・ダウンロードを行う際の助けにでもなれば幸いです。