前提
仕事で楽天FTPサーバに画像をアップロードする作業ができてしまって、毎回FTPソフト開くのが面倒だなと思い簡単なウェブアプリを作りました。
完成品
ファイルを選択し、アップロードボタンを押すだけ。あとはRailsくんが頑張ってFTPにあげるのを待つのみ。
画像は1枚でも複数枚でも対応できます。
この記事で言及しないこと
- 上記の画像プレビュー機能(
new FileReader
とかreadAsDataURL
でググると良記事がたくさん出てきます) - フロントはVue3で書いています。
@change
とか文法的なことは説明しない。 - axiosの使い方
フロント
html
<label for="image-uploader">ファイルを選択</label>
<input type="file" multiple accept=".jpg"
id="image-uploader" style="display:none;"
@change="onFileChanged"
/>
...
<button @click="uploadToFTP">アップロード</button>
multiple
: 複数のファイルを選択できるようにする
accept=".jpg"
: .jpg画像のみ受け付ける
style="display:none;"
: ファイル選択の窓は<label>
で発火させているので隠す
@change="onFileChanged"
: ファイル読み込み終わったらonFileChanged
関数を呼ぶ
JavaScript
const formData = new FormData(); // ......A
// データを準備
function onFileChanged(e) {
const files = e.target.files; // ......B
[...files].forEach((file) => { // ......C
formData.append("images[]", file); // ......D
})
}
// アップロードした画像をFTPにあげる
function uploadToFTP() {
axios.post("/Rails側で決めたパス", formData) // ......E
}
A
非同期的にデータを送るのでformDataをまずnewします。
B
読み込んだ画像データはe.target.files
から取り出します。
C
取り出したfiles
の中から画像データを一件ずつformDataに追加します。しかしこのfiles
はFileListという型のオブジェクトで、forEach
が使えません。なのでスプレッド構文で配列に変換します。
D
formData
に画像データを入れます。images[]
の”[]”が味噌です。
後ほどRails側のparamsからparams[:images]
で受け取ったものをeach
で繰り返し処理できるようになります。
”[]”が抜けると画像データ一件しか入らなくなります。
ここを参考:MDN-FormData.append()
E
axiosでPOSTします。
urlは自分で決めたルートで大丈夫です。(/api/upload/upload_imageとか)
バックエンド
Rails(コントローラ)
routes.rbなどは割愛します。
def upload_image
# ftpインスタンスを生成し、ログイン
ftp_host = 'upload.rakuten.ne.jp'
ftp = Net::FTP.new(ftp_host)
ftp.login(user = "xxx", passwd = "yyy")
# 画像用フォルダーを作る(「すでにそのフォルダーが存在してます」のエラーが起こりがちなので、エラーハンドリングをちゃんと書いた方がいいかも)
ftp.mkdir("/cabinet/images/xxxxxxx")
# 作業対象のフォルダーに入る
ftp.chdir("/cabinet/images/xxxxxxx")
# 画像をアップロード
image_files = params[:images]
image_files.each do |image|
ftp.putbinaryfile(image, image.original_filename)
end
# ログアウト
ftp.close
end
おわりに
formData.append("images[]", file);
のところ"[]"をつけるのが全然分からず苦労した。
次はアップロードの進捗バーとか、アップロード先をフロントで指定するなど色々実装したいのですが、今日はここまでです。