#初めに
画像投稿を実装するために、自分の技術力の問題もあり、血の涙が何度も出ました
もう僕のようなレベル感の人が画像投稿で涙しないように、
そして、画像投稿機能を実装する時にこういう問題があったなと思い出しやすいものになるように、
色々つまづいたので文量が多くなってしまいましたが、それらをシンプルさ意識してまとめてみました。
- 【画像の複数投稿について】
- 【複数画像のプレビュー】
- 【おまけ(リサイズ/画像の向き/バリデーション)】
以上の流れでまとめていきます!
最終的には以下の画像のような複数投稿プレビューができるようになるはずです!
⇩⇩⇩⇩⇩⇩⇩⇩
Rails上でpaperclip+rmagickで画像投稿機能は実装しました。
ちなみに、imagemagickの脆弱性は対応されているので無問題。
http://www.itmedia.co.jp/enterprise/articles/1605/06/news047.html
画像のプレビュー自体はhtmlとjavascriptしか関係ないです!
#【画像の複数投稿について】
画像の複数投稿は、画像の投稿機能さえ出来てしまえば簡単です!
- 画像の投稿
- 画像の複数投稿
以上の順でまとめていきます。
##1.画像の投稿
画像投稿の機能自体は、以下の2つをを参考に実装しました。
この部分について具体的な実装方法は以下のリンクを参照でお願いします。
非常に簡潔で分かりやすいです。
①Railsで、超簡単便利なPaperClipを使って、複数画像をS3にアップロード
②RailsでPaperclipを使ってファイルをアップロードする
自分は基本的に①を参考にして、
ファイルの保存先の部分だけ②の「3. Paperclipのアップロードファイルの保存先とValidation」を参考にしました。
①ではawsにファイルを保存する設定だったので、awsなしでローカルでも試せるような設定にするために②を参考にしました。
##2.画像の複数投稿
画像の複数投稿については、
Railsで、超簡単便利なPaperClipを使って、複数画像をS3にアップロード
にもあるのですが、
複数画像投稿を複数のフォームで実装する場合、以下のようにします。
<ul>
<li>ファイル1:<%= file_field_tag "images[]", type: :file %></li>
<li>ファイル2:<%= file_field_tag "images[]", type: :file %></li>
</ul>
そして、複数画像投稿を1つのフォームで実装する場合、以下のようにします。
<ul>
<li>ファイル複数選択:<%= file_field_tag "images[]", type: :file, multiple: true %></li>
</ul>
ビックリするくらい簡単なのですが、file_field_tag(input type="file")にmultiple: true(multiple)を追加するだけです。
このmultipleのデモや対応ブラウザの確認はココを参考しました。
#【複数画像のプレビュー】
- 画像1つのプレビュー
- 複数画像のプレビュー
以上の順でまとめます。
##1. 画像1つのプレビュー
このページでプレビューのためのコードやデモや対応ブラウザの確認ができます。以下のようになります。
⇩⇩⇩⇩⇩⇩⇩⇩
このページにあるコードにコメントをつけるなら・・・
HTML
<input type="file" onchange="previewFile()"><br> <!-- jsのpreviewFile()を呼ぶ -->
<img src="" height="200" alt="Image preview..."> <!-- プレビューの表示枠を用意 -->
JavaScript
function previewFile() {
var preview = document.querySelector('img');//どこでプレビューするか指定。'img[name="preview"]'などにすればimg複数あっても指定できます。
var file = document.querySelector('input[type=file]').files[0];//'input[type=file]'で投稿されたファイル要素の0番目を参照します。input[type=file]にmutipleがなくてもこのコードになります。
var reader = new FileReader();
reader.addEventListener("load", function () {
preview.src = reader.result;//めちゃめちゃ長い文字列が引き渡されます。ユーザーのファイルパスに紐付かない画像情報だと思います。
}, false);
if (file) {
reader.readAsDataURL(file);//ここでreaderのメソッドに引数としてfileを入れます。ここで、readerのaddEventListenerが発火します。
}
}
プレビューに関しては、
ちょっと違う書き方もあり、それはココ、
ユーザーのファイルパスがfakepathになり特定できないことがわかったこのページ、
とかが参考になりました。
##2. 複数画像のプレビュー
複数画像のプレビューを可能にさせるには、ココの以下のコードがポイントになります。
var file = document.querySelector('input[type=file]').files[0];
input[type=file]をmultipleにした時、ここのfiles配列に複数の画像の情報が入ります。
files[0]...files[1]...files[2]とイイ感じに、imgタグに貼り付ければokです!
実際の実装方法は、以下のリンクを参考にしてください!ちょっとコードが汚いかもしれませんがお許しください。
https://codepen.io/namitop/pen/VjxBLL
⇩⇩⇩⇩⇩⇩⇩⇩
#【その他(リサイズ/画像の向き/バリデーション)】
paperclip+rmagickで困った以下の3つについてまとめます!
これで、みなさんは画像投稿にもう涙することはないはず!
①リサイズ
②画像の向き
③バリデーション
###①リサイズ
リサイズはImageMagickを使ってできます。
実装はRailsで、超簡単便利なPaperClipを使って、複数画像をS3にアップロードにもあるのですが、has_attached_fileの:stylesでサイズ指定できます。
どのようにサイズが変更されるか、注意しないと縦横比がおかしくなってしまいます。どのようにリサイズされるか、「Rails4+Paperclip(ImageMagick)のサムネイルサイズ一覧」で図示されているのでチェックしましょう。
自分はユーザーのプロフィール画像で使った時は100×100#を使いました。元画像に対して、以下のようにリサイズされます。
自分は使わなかったですが、「RubyのRMagickで縦横比固定でリサイズしたり切り抜いたり」によると、元画像に対し、どの部分を切り抜くかをxy座標で指定できます。
###②画像の向き
「PaperclipでEXIF情報に従って画像を回転させる」にあるように、**「スマートフォンから投稿した横向きの写真が縦に表示される」**という現象が自分にも起きました。
これの原因はスマホでスマホを横にして写真を撮っても、何もしなければ、標準が縦のようで横に撮った写真は90度回転した状態で表示されます。
この問題を解決するには、画像のEXIF情報を利用して、撮った時のスマホの向きから、画像を回転する必要があります。これは、has_attached_fileに以下のコードを追加すれば解決できます。ちなみに、このorientでスマホの向きと反対に指定できたりします。
:convert_options => { :all => "-auto-orient" }
######一つだけ注意する点
「PaperclipでEXIF情報に従って画像を回転させる」には「:allで指定したオプションは全ての:stylesに適用されますが、originalの画像は未加工の状態で保存されます。」とあります。
stylesでoriginalを指定しなくても、originalには元画像が保存されるようになっているのですが、それに対してはこの向きを正す変換をしてくれません。僕はこれで血を見ました。気をつけましょう。
ただしstylesでoriginalを指定さえすれば、originalにも向きを正す変換をしてくれます。
###③バリデーション
画像ファイルだけ受け付けるようにする場合、「RailsのPaperclipについてまとめてみた」の「3. 添付ファイルに対するvalidation」を参考に、以下のようなコードにして、アップロードできるファイルを指定してあげましょう。
validates_attachment_content_type :sample_image, content_type: ["image/jpeg", "image/gif", "image/png"],message: 'ファイル形式が不正です。'
imageだけという指定方法もあるのですが、それだとsvgファイルも受け付けちゃうので、画像投稿でベクタ形式の画像を投稿するクレイジーな人はいるとは思いませんが、拡張子まで指定しておきましょう。