VueとRailsで画像投稿
前回の続きで、画像投稿機能を作成します
Active Storage とは
Active Storageは、ファイルアップロードを行うための機能です。
セットアップ
docker-compose run web rails active_storage:install
docker-compose run web rails db:migrate
echo 'gem "aws-sdk-s3"' >> web/Gemfile
docker-compose run web bundle
docker-compose exec web bash
apt install -y emacs
EDITOR=emacs bin/rails credentials:edit
aws:
access_key_id: AKIAJS7O5KJPISTNNTMQ
secret_access_key: 0pRlACR98i3BOyDei9rPaHRHLWZ3gKcE//WlNdhZ
s3:
region: ap-northeast-1
bucket: activestorage-sample-bucket
[web/config/storage.yml]
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: <%= Rails.application.credentials.dig(:aws, :s3, :region) %>
bucket: <%= Rails.application.credentials.dig(:aws, :s3, :bucket) %>
[web/config/environments/development.rb]
# ファイルをAmazon S3に保存する
config.active_storage.service = :amazon
[web/app/models/book.rb]
class Book < ApplicationRecord
has_one_attached :avatar
end
[web/app/controllers/books_controller.rb]
class BooksController < ApplicationController
before_action :set_book, only: [:show, :update, :destroy]
def index
@books = Book.all
render json: @books
end
def show
send_data @book.avatar.download,
filename: "sample.jpeg",
type: "image/jpeg",
disposition: "inline"
end
def create
file = params[:avatar]
@book = Book.new
@book.name = "flie"
@book.avatar = file
if @book.save
render :json => { id: @book.id }
else
end
end
def destroy
@book.destroy
end
private
def set_book
@book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:name, :avatar)
end
end
[vue/src/views/About.vue]
<template>
<div class="about">
<h1>投稿のサンプルページ</h1>
<input :value="imagefile" @change="selectedFile" type="file" name="file">
<button @click="upload" type="submit">アップロード</button>
<div v-for="(book, index) in books" v-bind:key="book.id">
<img :src="book.id | item">
<button v-on:click="destroy(book.id, index)">
削除
</button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default{
data: function () {
return {
books: [],
uploadFile: null,
addlist: null,
imagefile: null
}
},
created: function(){
axios
.get(`/books.json`)
.then(response => (this.books = response.data))
.catch(error => console.log(error))
console.log(this.books)
console.log(this.addlist)
},
methods: {
selectedFile: function(e) {
// 選択された File の情報を保存しておく
e.preventDefault();
let files = e.target.files;
this.uploadFile = files[0];
},
upload: function() {
// FormData を利用して File を POST する
let formData = new FormData();
formData.append('avatar', this.uploadFile);
let config = {
headers: {
'content-type': 'multipart/form-data'
}
};
axios
.post('books', formData, config)
.then(response => {
this.books.push({
id: response.data.id
});
this.imagefile = null;
this.uploadFile = null;
})
.catch(function(error) {
console.log(error)
})
},
destroy: function(id, index){
axios
.delete(`books/${id}.json`)
.then(response => {
this.books.splice(index, 1);
})
.catch(error => console.log(error))
}
},
filters: {
item: function(val){
return "/books/" + String(val)
}
}
}
</script>