Vue+Railsでファイルアップロード機能を作成してみる
今回はRailsとVueを使用してファイルアップロード機能について作成したいと思います。
開発環境
投稿記事を例に説明していきます。
投稿用のテーブル作成
投稿のタイトル、本文、サムネイル画像用のテーブルを用意します。
rails g model Post title:string content:string image_name:string
controllerの設定
アップロードするのに必要な箇所だけを載せます。
class PostsController < ApplicationController def new @post = Post.new end def create if params[:post][:image_name].present? @post = Post.new( title: params[:post][:title], content: params[:post][:content], image_name: params[:post][:image_name].original_filename, user_id: current_manager.id ) output_path = Rails.root.join('public/images', params[:post][:image_name].original_filename) File.open(output_path, 'w+b') do |fp| fp.write(params["post"]["image_name"].read) end else @post = Post.new( title: params[:post][:title], content: params[:post][:content], image_name: "no-image.png", ) end redirect_to root_path end end
DBにはparamsで取得したimage_nameを直接いれようとするとエラーになります。
アップロードされたファイル(params[:image_name)
は以下のようにActionDispatch::Http::UploadedFile
クラスとなっています。
=> #<ActionDispatch::Http::UploadedFile:0x00007fc3222860a8 @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"post[image_name]\"; filename=\"img_04.jpg\"\r\nContent-Type: image/jpeg\r\n", @original_filename="img_04.jpg", @tempfile=#<File:/var/folders/t0/pl2n_7gx5vdbc8w1dww0j8400000gn/T/RackMultipart20180721-5143-1qm64n6.jpg>>
params[:image_name].original_filename
でファイル名を取得します。
その後、画像を保存する場所のパスを作成します。
output_path = Rails.root.join('public/images', params[:post][:image_name].original_filename)
次に上記で取得したパスに画像を保存します。
File.open(output_path, 'w+b') do |fp| fp.write(params["post"]["image_name"].read) end
ファイルの読み込みと書き込み処理にはFileクラスがあります。
ファイル本体のデータを保存するためにread
メソッドを使用します。
これでrails側の設定は終わりです。
vueとrailsの連結部分
今回はrails側でform_tag
等は使用しないでvue側でpostリクエストをするのでrails側で作成したトークンをvueに渡す必要があります。
詳細は以前自分が書いた記事があるので参考にしてみてください。
vue側の設定
<template> <div class='wrapper'> <form action='/posts' method="post" enctype="multipart/form-data"> <input name="authenticity_token" type="hidden" :value="token"> <p class="postTitle"> <input type='text' placeholder="タイトル" class="postTitleField" v-model="post.title" name="post[title]" > </p> <input type="file" name="post[image_name]"> <p class="tag"><input type='text' class="tag_input" placeholder="タグを入力してください" name="tag_name"></p> <button class="releaseBtn">公開する</button> </form> </div> </template> <script lang="ts"> import { Vue, Prop } from "vue-property-decorator"; import Post from "./data/post"; export default class New extends Vue { @Prop() public post: Post; @Prop() public token: string; } </script>
ここでは親からアクセストークンをpropsで受け取りinputタグのvalueに代入しているところがないとinvalid tokenとエラーになります。
まとめ
以上でgemを使用しないで簡単なアップロードの機能ができました。 最初は画像データの中身が文字列として保存されてしまったりと躓きましたが、なんとか完成することができました。