カツオ @SpringBootとVue.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

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

関連記事

no image

【vue.js】画像ファイルのアドレスの指定方法について【vuetify】

assets配下に格納している画像ファイルの指定方法についてちょっとつまって調べました。個人的によく忘れちゃうので画像などの静的リソースの指定方法についてメモしておきます。 imgタグの場合 < …

no image

Javascriptでソート処理の実装(jQuery・カスタムデータ属性)

jQueryを使ったシンプルなソート機能のサンプルです。 こん感じでリスト要素があった時に、ボタンクリックで並び替え(ソート)処理を行います。 ※イメージのみです。動きません。 ソート 山田 鈴木 田 …