多文件上传和预览(图片和视频)

时间:2021-05-04 19:52:04

标签: javascript vue.js vuejs2

我有一个子组件,它有一个隐藏在开槽元素后面的输入字段。父级将提供带槽的点击事件元素,但也会显示文件的预览,并可以在上方删除它们。

我不确定在子/父关系之间工作时如何使用此选择和预览功能。

下面是我所得到的,但我现在对去哪里感到困惑。

该插槽用于触发子级中的事件,但在尝试实际按照当前编写的方式呈现事物时,我得到了 "TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'."。 我哪里出错了?

如果您需要更多信息,请告诉我!加油!

注意:我还需要使其与 V-model 兼容,但我目前不知道如何做到这一点。

UploadMediaFiles(子组件)

<template>
  <div class="upload-media-files">
    <input
      id="input-file"
      type="file"
      accept="*"
      multiple
      @change="addMedia"
      class="_add-media-input"
      ref="input"
    />
    <label for="input-file">
      <slot :openFileDialog="openFileDialog">
        <img
          src="https://www.clipartmax.com/png/middle/142-1422132_png-file-svg-upload-file-icon-png.png"
          alt=""
        />
      </slot>
    </label>
  </div>
</template>

<style lang="sass" scoped>
input
  display: none
</style>

<script>
export default {
  name: 'UploadMediaFiles',
  props: {
    multiple: { type: Boolean },
    accept: { type: String },
  },
  data() {
    return {
      files: [],
    }
  },
  computed: {},
  methods: {
    async addMedia(event) {
      const files = event.target.files || event.dataTransfer.files
      if (!files.length) return
      console.log(`files → `, files)
      this.files.push(files)
      this.$emit('selected', this.files)
    },

    openFileDialog() {
      this.$refs.input.click()
    },
  },
}
</script>

SelectAndPreviewFiles(父组件)

<template>
  <div class="select-and-preview-files">
    <div v-if="selectedFiles">
      <div :key="index" v-for="(selectedFile, index) in selectedFiles">
        <img :src="selectedFile" alt="" />
        <button @click="deleteFile(index)">Delete</button>
      </div>
    </div>
    <!-- <img />
      //OR
      <video /> -->
    <!-- <img :src="selectedFile" alt="" />-->
    <UploadMediaFiles @selected="(files) => selectFiles(files)" v-slot="{ openFileDialog }">
      <button @click="openFileDialog">
        <img
          src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Circle-icons-upload.svg/1200px-Circle-icons-upload.svg.png"
          alt=""
        />
      </button>
    </UploadMediaFiles>
  </div>
</template>

<style lang="sass" scoped>
img
  width: 20%
  margin: auto
  display: block
  margin-bottom: 10px
</style>

<script>
import UploadMediaFiles from '../atoms/UploadMediaFiles.vue'
export default {
  name: 'SelectAndPreviewFiles',
  components: {
    UploadMediaFiles,
  },
  props: {},
  data() {
    return {
      selectedFiles: [],
    }
  },
  computed: {},
  methods: {
    selectFiles(files) {
      this.selectedFiles.push(files)
      this.previewImage(files)
    },
    previewImage(files) {
      var vm = this
      for (var index = 0; index < files.length; index++) {
        var reader = new FileReader()
        reader.onload = function (event) {
          const imageUrl = event.target.result
          vm.files.push(imageUrl)
        }
        reader.readAsDataURL(files[index])
      }
    },
    deleteFile(index) {
      this.selectedFiles.splice(index, 1)
    },
  },
}
</script>

没有父子关系的 CodePen https://codepen.io/LovelyAndy/pen/gOmYGKO?editors=0001

1 个答案:

答案 0 :(得分:1)

问题是多个选定文件的 input 值是一个 FileList 数组,它本身是一个 File 对象列表。但是,previewImage() 似乎假定该值是一个 File 对象数组。

files[index] 实际上是一个 FileList,它不是 reader.readAsDataURL() 可接受的参数,从而导致错误。

要解决此问题,请迭代数组中的每个 FileList

export default {
  methods: {
    selectFiles(files) {
      this.selectedFiles.push(files);
      this.previewImage(files);
    },
    previewImage(files) {
      var vm = this
      for (var index = 0; index < files.length; index++) {
        const fileList = files[index]
        fileList.forEach(file => {
          var reader = new FileReader()
          reader.onload = function (event) {
            const imageUrl = event.target.result
            vm.selectedFiles.push(imageUrl)
          }
          reader.readAsDataURL(file)
        })
      }
    },
  }
}

demo