【Rails】PDFをダウンロードできる状態で投稿する方法


#はじめる前に：Cloudinaryに登録
様々なファイルを投稿するとき、どうしたらいいのかわからなかったので、画像投稿を参照してPDFを投稿してみました。

このリンクから登録をはじめましょう。
https://cloudinary.com/
右上のsign up for freeを押すと、登録画面に飛びます。


#手順

##Step1 ファイル投稿機能のためにMVCを準備
適当なファイルを使って、作っていきます。
今回は、Topicモデルを使っていきます。

###Topicsテーブルにカラムを追加

今回はファイルだけを投稿するためのアプリケーションなので、pdfカラムをString型で追加します。
(ファイルを保存するときにString型で指定するのはそういうものだと思っちゃいましょう)

今回はTopicsテーブルにstring型のpdfカラムを追加するので、

```:ターミナル
rails generate migration AddPdfToTopics pdf:string
```

と打ち込んで実行します。
すると、以下のようなマイグレーションファイルがタイムスタンプ付きで自動生成されます。

```ruby:db/migrate/20××××××××××_create_topics.rb
class AddPdfToTopics < ActiveRecord::Migration[6.0]
  def change
    add_column :topics, :pdf, :string
  end
end
```

書き込んだら、ターミナルでmigrateします。ターミナルで以下の操作を行いましょう。

``` 
$ rails db:migrate
```

これで、Topicモデルに、pdfという名前の、string型のカラムが追加できました。

###Viewの作成

まず、`index.html.erb`に以下のように追加で記述してください。

```ruby:app/views/topics/index.html.erb
    <% @topics.each do |t| %>
        
        <!--ここから-->
        <a href="<%= image_url t.pdf_url %>" target="_blank">
                <div class="post2-2"> 
                    <object data="<%= image_url t.pdf_url %>" type="application/pdf"></object>
                </div>
            </a>
        <!--ここまで-->

        <%= t.body %>
        <%= t.created_at %>
        
      </div>
    <% end %>
  </div>
```

実はデータベースにはファイルは保存されておらず、代わりにファイルが置かれる住所が保存されます。なので```t.image_url```のような書き方になります。

次に`topics controller`の一番下に以下のような記述があると思うので、それに「:pdf」の記述を付け加えましょう。

```ruby:topics_controller.rb
# 割愛

private

def topic_params
  params.require(:topic).permit(:body, :pdf)
end
```
これをしておかないと、pdfのパラメータが取得できず、投稿ができません。

また`new.html.erb`に以下を追加で記述してください。

```ruby:app/views/topics/new.html.erb

<%= form_for @topic do |f| %>
  <div class="field">
    <%= f.label :body %>
    <%= f.text_field :body, :size => 140 %>
  </div>

  <!--ここから-->
  <div class="field">
    <%= f.label :pdf %>
    <%= f.file_field :pdf %>
  </div>
  <!--ここまで-->

  <%= f.submit "投稿" %>
<% end %>

```
これはファイルを投稿するためのボタンを実装してくれるためのコードです。

file_fieldの場合は、以下の図のようなformが作られます。
ここにユーザーが投稿したいファイルを投稿することになります。
<img width="194" alt="スクリーンショット 2019-10-19 20.32.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202861/c8bb1859-d48b-1ee9-494c-52a967d2e7ad.png">
これで下準備は終わりました。次のStepからは実際のファイル投稿機能を実装していこうと思います。

##Step2. ファイル投稿機能
早速ここからファイル投稿機能を導入していこうと思います。

###gemの追加
今回はcloudinary、carrierwave、dotenv-railsというgemを使っていきます。Gemfileの一番下に以下を追加しましょう。

```ruby:Gemfile
gem 'carrierwave' , '~> 1.3' ,  '>= 1.3.1'
gem 'cloudinary'
gem 'dotenv-rails'
```
そしたらターミナルで

```:ターミナル
$ bundle install
```
と打ちこんでインストールします。

### アップローダーの作成
CarrierWaveのジェネレーターでアップローダーを作成します。以下のコマンドをターミナルに打ち込みましょう。

```:ターミナル
$ rails g uploader Pdf
```

uplpder の後ろは、先程追加したカラム名を入れてください。

### モデルの修正
Topicモデルを以下のように修正します。

```ruby:app/models/topic.rb
class Topic < ApplicationRecord

# 追記ここから
  mount_uploader :pdf, PdfUploader
# 追記ここまで

end
```

```mount_uploader :pdf, PdfUploader```はファイルを指定の場所に保存することを表します。

次に保存場所を指定するアップローダの設定です。

app/uploaders/pdf_uploader.rbの6~8行目を変更しましょう。

```ruby:app/uploaders/pdf_uploader.rb
  # Choose what kind of storage to use for this uploader:
  storage :file
  # storage :fog
```
上記を下図のように変えます。

```ruby:app/uploader/pdf_uploader.rb
  # Choose what kind of storage to use for this uploader:
  if Rails.env.production?
    include Cloudinary::CarrierWave
    CarrierWave.configure do |config|
      config.cache_storage = :file
    end
  else
    storage :file
  end
  # storage :fog
```

cloudinaryは外部のストレージサービスです。本番環境(リリース後=production)ではcloudinaryにファイルが保存され、それ以外(開発環境=ローカル)では自分のpcに保存されます。(publicフォルダー内に保存されます)

###APIキーの非公開

Cloudinaryの各アカウントには「Cloud name」、「API Key」、「API Secret」というアカウント固有の秘密のIDのようなものが付与されています。
この情報は非常に機密性が高く決して外部に漏らしてはいけない情報なので、これらの値が間違って公開されないような処理を施す必要があります。
のでこの節ではWebサービスを公開した時にそれらの値を公開しないようなコードを書いていくことにします。

三つの値は以下から確認することができます。
[Cloudinaryマイページ](https://cloudinary.com/console/welcome)

<img width="1138" alt="Cloudinary_Management_Console_-_Dashboard_png.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202861/85d4bf4e-ec96-553a-d50d-6fce0f8e4489.png">


`.env`というファイルを**アプリケーションディレクトリ(appやdbやGemfileがあるディレクトリ)**に自分で作成します。
以下の図のようになっていればオッケーです。
※vendorフォルダーの中ではないので注意してください。

<img width="1434" alt="スクリーンショット_2018-10-17_2_03_28.png" src="https://qiita-image-store.s3.amazonaws.com/0/301142/9c267a60-4ba5-7281-447a-f2504e59d892.png">

次に作成した`.env`ファイルに以下を入力します。



```:.env
CLOUD_NAME=q0w9e8r7t6yu5  #←この値は人によって違います！！
CLOUDINARY_API_KEY=123456789012345 #←この値は人によって違います！！
CLOUDINARY_API_SECRET=1a2s3d4f5g6h7j8k9l0a1s2d4f5g6h1q #←この値は人によって違います！！
```

ここで「=」の後のそれぞれの値は先ほどの[Cloudinaryのマイページ](https://cloudinary.com/console/welcome)で取得したキーに書き換えておいてください（自分が取得したキーは絶対他言しないように！！また数字は個々人によって変わります）。
また、書き換える際は、**コメントアウト部分を削除**してください！

　最後に隠しておきたいデータを定義した.envファイルをみんなに公開してしまっては意味が無いのでこのファイルは公開しないようにします。アプリケーションディレクトリにある`.gitignore`に下記を追加します。
<font color="red">**※もし`.gitignore`ファイルがない場合はアプリケーションディレクトリーにて作りましょう！**</font>

```:.gitignore
# 省略

/.env
```

これでOKです！

###APIキーの利用

最後の手順です！
まずはconfigフォルダに`cloudinary.yml`ファイルを作成してください。

<img width="932" alt="スクリーンショット_2019_10_20_1_11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202861/0cd104d2-332b-f681-380a-1f0d39a2e7ab.png">

config/cloudinary.ymlに以下のようにそのままコピペしてください。

```ruby:config/cloudinary.yml

development:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
production:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
test:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
```

これでAPIキーを非公開にしたまま利用することができました。


#PDFのダウンロード

PDFをダウンロードするときは、表示されているファイルを右クリックして、保存することができます！

#ファイル内のPDFを表示させる
`app/assets/images`内に、PDFファイルを入れます。

![スクリーンショット 2021-04-16 20.11.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1317576/bccb6f70-98fc-b1af-621b-b360218b8ff3.png)

その後、表示したいページのviewに、下の文章を入れます！

```
<object data="../../../assets/07.pdf" type="application/pdf" width="100%" height="100%"></object>
```