vue2实现图片&pdf预览,基于文件流、blob和createObjectURL

565 阅读1分钟

开发过程中被要求实现pdf的预览,当时也正好没有文件系统,于是做了一个可以预览PDF和图片的组件,仅供参考

原理

1、通过获取文件后端下载接口返回的文件流,通过设置请求的responseType: 'blob'接收。
2、通过const blob = new Blob([res.data], { type: this.headerFileType })将请求返回的文件流转换为blob.
3、window.URL.createObjectURL(blob)生成预览的URL
4、通过<img alt="img" :src="blobPath || url" />元素预览

另:PDF通过ifram标签预览<iframe :src="url"></iframe>

废话不多说上代码

预览组件

<template>
  <div class="content" v-loading="loading">
    <div v-if="isPdf" class="pdf-name">{{ fileName || $route.query.fileName }}</div>
    <iframe v-if="isPdf" :src="url" :style="iframeStyle" title="iframe"></iframe>
    <div v-else style="text-align:center;">
      <img alt="img" :src="blobPath || url" :style="imgStyle" @dblclick="imgDblclick" />
    </div>
  </div>
</template>
<script>

import TabContent from '@/components/wealth/layout/tab-content.vue'
import axios from 'axios'
export default {
  components: { TabContent },
  props: {
    fileId: String, // 文件ID
    fileName: String,
    fileType: { // 预览的文件类型 pdf、jpg、png、jpeg 
      type: String,
      default: 'pdf'
    },
    previewUrl: { // 默认产品中心 文件下载
      type: String,
      default: '/api/wp-product/v1/wp-product-file/downLoadFile/'
    },
    blobPath: { // blob文件流path
      type: String,
      default: ''
    },
    imgStyle: {
      type: Object,
      default: () => ({
        width: '600px',
        height: '600px'
      })
    },
    iframeStyle: {
      type: Object,
      default: () => ({
        width: '100%',
        height: '100%'
      })
    }
  },
  data() {
    return {
      // 目前只支持get请求
      url: '',
      loading: false
    }
  },
  watch: {
   // 单独使用时候,传入fileId自动查询文件流生成为blobPath
    fileId() {
      this.getFileBuffer()
    }
  },
  computed: {
    isPdf() {
      return this.fileType === 'pdf'
    },
    headerFileType() {
      return this.isPdf ? 'application/pdf;charset-UTF-8;' : 'image/jpeg;charset-UTF-8;'
    }
  },
  methods: {
    getFileBuffer() {
      this.loading = true
      axios({
        method: 'get',
        url: `${window.location.origin}${this.previewUrl}${this.$route.query.fileId || this.fileId}`,
        responseType: 'blob'
      }).then(res => {
        const blob = new Blob([res.data], {
          type: this.headerFileType
        })
        this.url = window.URL.createObjectURL(blob)
      }).finally(() => this.loading = false)
    },
    // 图片双击
    imgDblclick() {
    window.open(this.blobPath || this.url)
    },
    init() {
      if (this.blobPath) return
      this.getFileBuffer()
    }
  },
  created() {
    this.init()
  }
}
</script>
<style lang="less" scoped>
.content {
  width: 100%;
  height: 100%;

  .pdf-name {
    position: absolute;
    color: white;
    top: 38px;
    left: 85px;
    width: 300px;
    background-color: #313537;
    height: 18px;
    line-height: 18px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
</style>

在父组件中使用

// 接受生成的blobPath
<pdf-preview :blobPath="blobPath" fileType="png"></pdf-preview>

// 获取图片文件流
    async getFileBuffer(file, index) {
      await axios({
        method: 'get',
        url: `${window.location.origin}${this.previewUrl}${file.id}`,
        responseType: 'blob'
      }).then(res => {
      /*
      * 转换为blob流
      */
        const blob = new Blob([res.data], {
          type: 'image/jpeg;charset-UTF-8;'
        })
        /*
          * window.URL.createObjectURL(blob)
          * 通过blob流以及createObjectURL生成预览的blobPath
          */
        this.$set(this.fileList[index], 'blobPath', window.URL.createObjectURL(blob))
      })
    }