カツオ @FirebaseとVue.jsとちょっとNode.jsでWebサービス開発中

起業、転職、サラリーマン生活、Webサービス、アプリ、働き方、プログラミング、Java、JavaScript

javascript vue.js

【Vue.js】ファイルアップロード(複数ファイル)の渡し方(axios)

投稿日:

Vue.jsをつかって開発をやっています。画像アップロードを作りたくて、この2日くらい色々調べながら実装をしていました。ようやくそれなりに動くものができましたので、せっかっくなのでブログにも記載させていただきます。

Vue.jsで作りたい画面仕様

  • フロントはVue.js、axiosを使用
  • フロントで複数画像をアップロードし、サーバーにpostする処理とする。
  • 画像だけでなくて、テキスト情報も一緒に渡す
  • アップロードした画像はフロントにて表示され確認可能。

画面イメージ

ソースコード テンプレート

<template>
<div>
~~省略~~
</v-card-text>
<v-card-actions>
<v-btn flat icon @click="pickImage"><v-icon class="primary--text">add_a_photo</v-icon></v-btn>
<input type="file" style="display: none;" ref="image" v-on:change="onFileChange" mulitple="multiple">
</v-card-actions>
<v-card-text>
<div v-for="(uploadedImage,id) in uploadedImagesForView" class="uploadImage" :key="id">
<v-btn fab dark small color="primary" @click="clearImage(id)">
<v-icon>clear</v-icon>
</v-btn>
<img :src="uploadedImage" />
</div>
<div v-show="uploadedImagesForView.length>0" class="uploadImage">
<v-btn fab color="primary" @click="pickImage"><v-icon>add_a_photo</v-icon></v-btn>
</div>
</v-card-text>
</v-card>
<p class="container">
<v-btn block  large class="red" v-bind:class="{'accent-1' : post.content.length ==0}" dark @click="postContent"><v-icon>edit</v-icon>&nbsp;投稿する</v-btn>
</p>
</div>
</template>

ポイントは以下

  • input type=”file”をdisplay:noneで配置。
  • ファイルアップロード用のボタン(アイコン)をクリックすると、jsでこのタグをクリックさせる。
  • uploadedImagesForViewにアップされた画像ファイルを追加する。1件以上あれば表示される。

ソースコード スクリプト

<script>
import axios from 'axios'
let fileMaxSize = 2000000
let fileMaxCount = 5
let fileExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif']
export default {
name: 'PostView',
data () {
return {
post: {
type: '',
content: '',
uid: '',
uploadedImages: []
},
dialog: false,
uploadedImagesForView: [],
isError: false,
errorMessage: ''
}
},
mounted () {
var user = firebase.auth().currentUser
this.post.uid = user.uid
},
methods: {
postContent: function () {
// 入力がなければ何もしない。
if (this.post.content.length === 0 || this.post.type === '') {
return
}
this.dialog = true
this.addPost()
},
setType: function (tmp) {
this.post.type = tmp
},
addPost: async function () {
let config = {
headers: {
'content-type': 'multipart/form-data'
}
}
var formData = new FormData()
formData.append('type', this.post.type)
formData.append('content', this.post.content)
formData.append('uid', this.post.uid)
for (var i = 0; i < this.post.uploadedImages.length; i++) {
var name = 'uploadedImages[' + i + ']'
formData.append(name, this.post.uploadedImages[i])
}
await axios.post('http://localhost:20000/addPost', formData, config)
.then(response => {
this.dialog = false
alert('Thank you !! 投稿しました')
this.$router.push('/')
})
.catch(function (error) {
console.log(error)
})
},
// ファイル選択を表示
pickImage () {
this.$refs.image.click()
},
// ファイルアップが完了したら動作
onFileChange (e) {
let files = e.target.files || e.dataTransfer.files
// ファイルのチェック
// ファイルタイプ
var myFileType = files[0].type
var fileTypeCheck = false
for (var i = 0; i < fileExtensions.length; i++) {
var fileExtension = fileExtensions[i]
if (myFileType.indexOf(fileExtension) > -1) {
fileTypeCheck = true
break
}
}
if (!fileTypeCheck) {
this.isError = true
this.errorMessage = 'アップロードできるファイルは画像のみです。'
return
}
// ファイルサイズ
if (files[0].size > fileMaxSize) {
this.isError = true
this.errorMessage = 'ファイルサイズが大きすぎます。'
return
}
// ファイル枚数
if (this.post.uploadedImages.length + 1 > fileMaxCount) {
this.isError = true
this.errorMessage = 'アップロードできるファイルは最大' + fileMaxCount + 'ファイルです。'
return
}
this.post.uploadedImages.push(files[0])
this.createImage(files[0])
},
// アップロードした画像を表示
createImage (file) {
let reader = new FileReader()
reader.onload = (e) => {
var uploadedImage = e.target.result
this.uploadedImagesForView.push(uploadedImage)
}
reader.readAsDataURL(file)
},
clearImage (id) {
this.uploadedImagesForView.splice(id, 1)
this.post.uploadedImages.splice(id, 1)
}
},
components: {
}
}
</script>
  • 画像の入力チェックもしているので記載が長くなっている。
  • formData.appendで画像ファイルを追加してるのだが、ここでちょっとはまった。画像を格納した配列を直接appendするとうまく動作しない。1ファイルずつappendすると正常に動作する。

NG例

this.post.uploadedImages が配列だとして、、、
this.post.uploadedImages.push(imagefile01)
this.post.uploadedImages.push(imagefile02)
this.post.uploadedImages.push(imagefile03)
formData.append(this.post.uploadedImages)
※事前にリストに格納し、リストをformDataにappend

OK

配列を1ファイルずつappendしていく。
名前をarray[0]のように配列の値の指定方法で設定してやることで、サーバー側でリストとして認識できるよう。
for (var i = 0; i < this.post.uploadedImages.length; i++) {
var name = 'uploadedImages[' + i + ']'
formData.append(name, this.post.uploadedImages[i])
}

なぜこのパターンだとうまくいくのが理解しきれていないが、一応、これで正常に動いている。

同じように悩んでいる人の力になれればいいと思う。

-javascript, vue.js

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

【Vue.js】サーバーに公開する方法(ビルド手順)

作ったプロジェクトをサーバーに配置する方法です。この手順ですと静的リソースとしてローカルでjavascript、cssをビルドしてくれますので、安いレンタルサーバーとかでも動作します。私も昔から使って …

【vue.js】コンポーネント間のメソッドの呼び出し|親から子(ref)、子から親(emit)

Vue.jsで別コンポーネントに定義しているメソッドの呼び出し方についてメモしておきたいと思います。別コンポーネントの呼び出しは大きく2つ存在します。 親から子(ref) 子から親(emit) 最初に …

【Vue.js】v-forの中でrefを使う($refsの指定方法)

Vue.jsのref便利ですよね。親コンポーネントから子コンポーネントの値や関数を使うことができます。今回、v-forを使って繰り返し処理で動的に生成したコンポーネントをrefで操作しようとしてけっこ …

【Vue.js】ドラッグアンドドロップの実装方法(vuedraggable)

vue.jsでドラッグアンドドロップの実装ですが、「vuedraggable」を利用することで優れたUIを簡単に実装することができます。 vuedraggableの使い方については以下サイトがとても丁 …

【vue.js】【メモ】vue.jsのプロジェクトの作成コマンド