vue中内外网实现文件上传和下载

421 阅读3分钟

在日常开发后台系统中,经常会涉及到文件图片上传,导入和文件下载。比如更换头像,批量导入数据,下载模版等等。上传地址环境一般分为公司内网和外网(如七牛云、阿里云),不同环境实现过程也是有区别的,在这里记录一下平时实现较多的方式,已便后期查阅。如有错误请多评论指正

内网环境

前言:为了提高开发效率和页面结构美观,直接选用UI库中上传组件,拿来即用,根据自己业务需求稍作修改即可。笔者写后台主要使用Ant Deaign Vue 和 Element UI

1.上传文件

  • template 

    <a-upload
      name="file"
      :file-list="fileList"
      action="#"
      :custom-request="customRequestUpload"
      :headers="headers"
      :remove="removeFiles"
      :before-upload="beforeUpload"
      @change="handleUploadChange"
    >
      <a-button
        :disabled="fileList.length>=20||fileList.some(s=>{return s.status==='uploading'})"
        size="small"
        type="dashed"
      >
        <a-icon type="upload" /> 点击上传
      </a-button>
      <div style="color:#999;font-size:12px;marginTop:4px;">注:最多上传20个附件,每个附件不可大于200MB</div>
    </a-upload>
    
  • javascript

    data() {
        return {
          headers: {
            authorization: 'authorization-text'
          },
          fileList: []
        }
    }
    

// 上传前钩子
beforeUpload(file) {
    const isLt2M = file.size / 1024 / 1024 < 200
    if (!isLt2M) {
        this.$message.error('文件大小不能超过200M!')
    }
    return isLt2M
},
// 上传文件状态改变触发,处理上传中加载显示
handleUploadChange(info) {
    this.fileList = info.fileList.filter(f => f.status)
},
// 自定义上传方法
customRequestUpload(file) {
    const formData = new FormData()
    formData.append('file', file.file)
    // 请求接口
    uploadFiles(formData).then((res) => {
        if (res.code === 0) {
            this.fileList.push({
                uid: `-${+new Date()}`,
                status: 'done',
                name: res.rows.fileName,
                url: res.rows.downloadUrl,
                fileMime: res.rows.contentType // 类型
            })
            this.fileList = this.fileList.filter((f) => f.status === 'done')
            this.$message.success('上传成功')
        } else {
            this.$message.error(res.msg)
            this.fileList = this.fileList.filter((f) => f.status === 'done')
        }
    }).catch(() => {}).finally(() => {})
}

2.导入文件

  • template

    <el-upload
      action="#"
      :http-request="httpRequest"
      :before-upload="beforeAvatarUpload"
      :file-list="fileList"
      :before-remove="beforeRemove"
    > 
        <el-button size="small" class="blue-btn" type="primary" icon="el-icon-upload">
          上传
        </el-button>
    </el-upload>
    
  • javascript

    data() {
        return {
            fileList: [] 
       }
    }
    
    // 文件上传之前的钩子
    beforeAvatarUpload(file) {
        const fileReader = new FileReader() // FileReader读取文件内容
        fileReader.readAsDataURL(file) // 转换成base64编码
        const _that = this
        fileReader.onload = () => {
            _that.uploadData.file_data = fileReader.result.split('base64,')[1]
            _that.uploadData.file_name = file.name
            // 请求接口
            _that.$fetch(_that.$api.uploadUrl, _that.uploadData).then(res => {
                if (res.code === '0000') {
                    const result = []
                    result.push({
                        key: res.data.file_name,
                        name: file.name
                    })
                    _that.fileList = result
                }
            }).catch(() => { })
        }
    },
    httpRequest(data) {},
    beforeRemove(file, fileList) { // 删除已上传的文件钩子
        for (let i = 0; i < this.fileList.length; i++) {
            if (this.fileList[i].name === file.name) {
                this.fileList.splice(i, 1)
            }
        }
    }
    

外网环境

1.上传头像到七牛云

<!-- 更换头像 -->
<el-upload
  :action="domain"
  :data="QiniuData"
  :show-file-list="false"
  :on-success="handleAvatarSuccess"
  :before-upload="beforeAvatarUpload"
>
    <el-avatar :size="100">
        <img v-if="imageUrl" :loading="loading" :src="imageUrl" alt="">
        <img v-else src="../../assets/img/avatar3.jpg">
    </el-avatar>
    <i class="el-icon-edit" />
</el-upload>

data() {
    return {
        QiniuData: {
            key: '', // 图片名字处理
            token: '' // 七牛云token
        },
        domain: 'https://upload.qiniup.com', // 七牛云的上传地址(华东区)
        qiniuaddr: 'https://files.xxxx.cn', // 你的七牛云的图片外链地址
        imageUrl: this.$store.state.user.avatar, // 头像路径
        myDatas: { // 要传给后端的数据字段
            user_token: getToken(),
            avatar_uri: ''
        }
    }
}

// 头像上传
handleAvatarSuccess(response) { // 文件上传成功时的钩子
    this.imageUrl = `${this.qiniuaddr}/${response.key}`
    this.myDatas.avatar_uri = response.key
    this.changePhoto()
    this.resetSetItem('avatar', JSON.stringify(`${this.qiniuaddr}/${response.key}`))
},
beforeAvatarUpload(file) { // 上传文件之前的钩子
    const isPNG = file.type === 'image/png'
    const isJPEG = file.type === 'image/jpeg'
    const isJPG = file.type === 'image/jpg'
    const isLt2M = file.size / 1024 / 1024 < 2
    if (!isPNG && !isJPEG && !isJPG) {
        this.$message.error('上传头像图片只能是 jpg、png、jpeg 格式!')
        return false
    }
    if (!isLt2M) {
        this.$message.error('上传头像图片大小不能超过 2MB!')
        return false
    }
    // return isPNG && isJPEG && isJPG && isLt2M
    return this.$fetch(this.$api.getQinniuToken, { user_token: getToken() }).then(res => {
        this.QiniuData.token = res.data
        this.QiniuData.key = moment(new Date()).format('YYYYMMDDHHmmss') + file.name // key为上传后的文件名
    })
},
changePhoto() { // 提交-更新头像
    // this.loading = true
    this.$fetch(this.$api.updateAvatar, this.myDatas).then(res => {
        if (res.code === '0000') {
            this.loading = false
            Message({
                message: res.toast,
                type: 'success',
                duration: 3 * 1000
            })
        }
    }).catch(err => {
        this.loading = false
        console.log(err)
    })
}

main.js
// 监听
localStorageVue.prototype.resetSetItem = function(key, newVal) {
    if (key === 'avatar') {
        // 创建一个StorageEvent事件
        var newStorageEvent = document.createEvent('StorageEvent')
        const storage = {
            setItem: function(k, val) {
                localStorage.setItem(k, val)
                // 初始化创建的事件
                newStorageEvent.initStorageEvent('setItem', false, false, k, null, val, null, null)
                // 派发对象
                window.dispatchEvent(newStorageEvent)
            }
        }
    return storage.setItem(key, newVal)
}}

2.上传文件到七牛云

<el-form-item ref="upload" label="上传" prop="item_template">
    <el-upload
        action="https://upload.qiniup.com"
        :data="QiniuData"
        :file-list="form.item_template"
        :before-remove="beforeRemove"
        :on-success="handleAvatarSuccess"
        :before-upload="beforeAvatarUpload"
    >
        <el-button size="small" class="green-btn" type="primary">
            点击上传</el-button>
    </el-upload>
</el-form-item>

data() {
    return {
        QiniuData: {
            key: '', // 文件名字处理
            token: '' // 七牛云token
        },
        qiniuaddr: 'https://files.xxxx.cn', // 你的七牛云的外链地址
        item_template1: [], // 传后端的数据
        fileName: ''
    }
}

handleAvatarSuccess(response) { // 文件上传成功时的钩子
    // 创建-要传递的模板json字符串
    const item = { name: '', key: '' }
    item.name = this.fileName
    item.key = response.key
    this.item_template1.push(item)
    this.form.item_template.push(item)
    this.$refs.upload.clearValidate()
},
beforeAvatarUpload(file) { // 上传文件之前的钩子
    return this.$fetch(this.$api.getQinniuToken, { user_token: getToken() }).then(res => { 
                this.QiniuData.token = res.data
                this.QiniuData.key = `${uuid.v1()}/${file.name}` // key为上传后的文件名
                this.fileName = file.name
            })
},
beforeRemove(file, fileList) { // 删除文件之前的钩子
    for (let i = 0; i < this.item_template1.length; i++) {
        if (this.item_template1[i].name === file.name) {
            this.item_template1.splice(i, 1)
        }
    }
    for (let i = 0; i < this.form.item_template.length; i++) {
        if (this.form.item_template[i].name === file.name) {
            this.form.item_template.splice(i, 1)
        }
    }
}

下载文件流

封装方法

export function fetchGet(url, params, wordName) {
    const _params = params ? httpMd5(params) : {}
    const word_name = `${wordName}-${timestampToTime(new Date())}${Math.floor(Math.random() * 100)}`
    return axios({
        method: 'GET',
        url: `/api${url}`,
        params: _params,
        responseType: 'blob'
    }).then(res => {
        const blob = new Blob([res.data], {
            type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=utf-8'
        })
        const downloadElement = document.createElement('a')
        const href = window.URL.createObjectURL(blob) // 创建下载的链接
        downloadElement.href = href
        downloadElement.download = word_name + '.docx' // 下载后文件名
        document.body.appendChild(downloadElement)
        downloadElement.click() // 点击下载
        document.body.removeChild(downloadElement) // 下载完成移除元素
        window.URL.revokeObjectURL(href) // 释放掉blob对象
    }).catch(err => {
        console.log(err)
    })
}

export function fetchGetLog(url, params, wordName) {
    const _params = params ? httpMd5(params) : {}
    const word_name = `${wordName}-${timestampToTime(new Date())}${Math.floor(Math.random() * 100)}`
    return axios({
        method: 'GET',
        url: `/api${url}`,
        params: _params,
        responseType: 'blob'
    }).then(res => {
        const blob = new Blob([res.data], { type: 'application/json;charset=utf-8' })
        const downloadElement = document.createElement('a')
        const href = window.URL.createObjectURL(blob) // 创建下载的链接
        downloadElement.href = href
        downloadElement.download = word_name + '.log' // 下载后文件名
        document.body.appendChild(downloadElement)
        downloadElement.click() // 点击下载
        document.body.removeChild(downloadElement) // 下载完成移除元素
        window.URL.revokeObjectURL(href) // 释放掉blob对象
    }).catch(err => {
        console.log(err)
    })
}

export function fetchGetJson(url, params, wordName) {
    const _params = params ? httpMd5(params) : {}
    const word_name = `${wordName}-${timestampToTime(new Date())}${Math.floor(Math.random() * 100)}`
    return axios({
        method: 'GET',
        url: `/api${url}`,
        params: _params,
        responseType: 'blob'
    }).then(res => {
        const blob = new Blob([res.data], { type: 'application/json;charset=utf-8' })
        const downloadElement = document.createElement('a')
        const href = window.URL.createObjectURL(blob) // 创建下载的链接
        downloadElement.href = href
        downloadElement.download = word_name + '.json' // 下载后文件名
        document.body.appendChild(downloadElement)
        downloadElement.click() // 点击下载
        document.body.removeChild(downloadElement) // 下载完成移除元素
        window.URL.revokeObjectURL(href) // 释放掉blob对象
    }).catch(err => {
        console.log(err)
    })
}

export function fetchGetXlsx(url, params, wordName) {
    const _params = params ? httpMd5(params) : {}
    const word_name = `${wordName}-${timestampToTime(new Date())}${Math.floor(Math.random() * 100)}`
    return axios({
        method: 'GET',
        url: `/api${url}`,
        params: _params,
        responseType: 'arraybuffer' // 返回数据的格式
    }).then(res => {
        const blob = new Blob([res.data], {
            type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'
        })
        const downloadElement = document.createElement('a')
        const href = window.URL.createObjectURL(blob) // 创建下载的链接
        downloadElement.href = href
        downloadElement.download = word_name + '.xls' // 下载后文件名
        document.body.appendChild(downloadElement)
        downloadElement.click() // 点击下载
        document.body.removeChild(downloadElement) // 下载完成移除元素
        window.URL.revokeObjectURL(href) // 释放掉blob对象
    }).catch(err => {
        console.log(err)
    })
}

引用

// 目标域名log下载
targetDomainLog(ID, Domian) {
    this.$fetchGetLog(this.$api.targetDomainLog, { user_token: getToken(), target_id: ID }, `${this.taskName}-目标域名/网站${Domian}`)
}

其他

main.js将方法注册到vue原型
import { fetchGet, fetchGetLog, fetchPost, fetchGetXlsx, fetchGetJson } from './utils/request'
Vue.prototype.$fetchGet = fetchGet
Vue.prototype.$fetchGetLog = fetchGetLog
Vue.prototype.$fetchPost = fetchPost
Vue.prototype.$fetchGetXlsx = fetchGetXlsx
Vue.prototype.$fetchGetJson = fetchGetJson