はじめに
Reactでファイルアップロードを実装してみたくて、どのような方法があるのか調べてみた。
あまり深く調べてはないが、1つめに良さそうだと感じた react-dropzone を使ってみたら、
これがかなりいい!!
あまりに気に入ってしまったので、react-dropzoneでアップロード対象のファイルを選んで、
axiosでPOSTして、サーバーサイドのRailsでファイルを保存するところまでをざっと紹介します。
Reactのことは分かっている前提ですすめますので、あしからず。
出来上がりのイメージはこんな感じ
画面左上に ファイルをドロップする場所があり、その右側にドロップされたファイルを列挙する場所がある。
で、画面下部にアップロード対象ファイルのプレビュー表示。

npm で必要なものをインストール
[bash]
$ npm install –save react-dropzone axios
[/bash]
アップロード画面実装
一気に貼り付けちゃいましたので、ざっと説明しておきます。
DropZone タグで、ドラッグドロップでファイルをアップロードできる機能が
できちゃいます。acceptプロパティで受け付ける画像の種類(ファイルの種類)を指定し、
onDropでドラッグドロップされた時の処理を実装します。
今回はonDropで受付可能なファイル、不可能なファイルを引数で受け取り、
ステートを変えて、ドロップ領域の右側にそれらのファイル名を列挙します。
と同時に、画面下部に受付可能なファイルのプレビューを表示しちゃいます。
これがいとも簡単なんです。びっくりですよ。
アップロードボタンがクリックされたら、FormData オブジェクトを作り、
そこにアップロード対象のファイルを詰め込んで、axiosでPOSTします。
たったこれだけなんですよ、ホント便利ですね。
何個のファイルがアップロードされるかは不定なので、
file_1、file_2、file_3のような連番の名前をつけておきます。
[bash]
import React from “react”
import DropZone from “react-dropzone”
import axios from “axios”
import {HOST_NAME} from “../constants/index”
class Upload extends React.Component {
constructor(props){
super(props)
this.state = {acceptedfiles: [],rejectedfiles: []}
}
handleDrop(accepted, rejected) {
this.setState({acceptedfiles: accepted, rejectedfiles: rejected})
}
handleOpenClick() {
this.dropzone.open()
}
handleUpload() {
let data = new FormData()
data.append(‘name’, ‘Tom’)
data.append(‘age’, ’28’)
let cnt = 1
this.state.acceptedfiles.map(file =>{
data.append(‘file_’ + cnt, file)
cnt+=1
})
axios.post(HOST_NAME + ‘/upload_image’, data)
}
render(){
let prev = []
this.state.acceptedfiles.map(f =>
prev.push(
)
)
return (
Hello Upload…
this.dropzone = node}
accept=”image/jpg,image/png”
onDrop={(accepted, rejected)=> this.handleDrop(accepted, rejected)}
>
ここにファイルをドロップしてください。
Accepted files…
{this.state.acceptedfiles.map(f =>
- {f.name} – {f.size}
)}
Rejected files…
{this.state.rejectedfiles.map(f =>
- {f.name} – {f.size}
)}
)
}
}
export default Upload
[/bash]
サーバーサイドの実装
Railsの実装になります。
まず最初に渡ってきたパラメーター(:name)に従ってフォルダを作ります。
今回の例では Tomさん固定です。
で、file_1、file_2、file_3と連番でアップロードされてきたファイルを
そのフォルダに保存します。
ね、簡単でしょ。
[bash]
def upload_image
image_path = Rails.root.to_s + “/public/upload_images/” + params[:name]
FileUtils.mkdir_p(image_path) unless FileTest.exist?(image_path)
cnt = 1
while true do
break if params[“file_” + cnt.to_s].blank?
file = params[“file_” + cnt.to_s]
fname = file.original_filename
File.open(image_path + “/#{fname}”, ‘wb’) { |f|
f.write(file.read)
}
cnt += 1
end
end
[/bash]
デザインCSS
とりあえず、適当ですが、こんな感じで。
[bash]
div.uploadFile{
float: left;
width: 260px;
}
div.preview {
float: left;
padding: 10px;
border: 1px solid #cccccc;
}
br.clear {
clear: both;
}
[/bash]
あとがき
出来上がってみれば、本当に簡単に出来てしまったように見えるのですが・・・
画像をPOSTした時
Rails側で
– 422 unprocessable entity
– Invalid form authenticity token
というエラーが出て悩まされました。CSRFについては分かってるつもりだったのですが、
分かってなかったようです。
ReactでSPAを作る時なんかは JSをサーブするサーバーと、APIをサーブするサーバーが
別になると思うので、
gem rack-cors を導入して、信用できるドメインを絞り込んで APIを受け付ける。
で、CSRFは ApplicationControllerで
– protect_from_forgery with: :null_session
として、とりあえず何でも受け付けるようにする。という感じでしょうか。
Leave a comment