前端文件上传、预览与下载

一.文件上传

文件上传是在前端开发过程中很常见的功能,这里介绍的是vue2中常使用的文件上传方式,适用于引入element-ui组件的情况下,使用的是element中的el-upload组件。

1.文件上传组件

把文件上传的方法封装成组件,这样后面使用的时候仅需要两三行代码即可,这里文件类型、大小、图片数量等的参数后续可以自己定义,这些参数放入props中。

<template>
  <div class="component-upload-image">
    <el-upload
      :action="uploadImgUrl"
      list-type="picture-card"
      :on-success="handleUploadSuccess"
      :before-upload="handleBeforeUpload"
      :limit="limit"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      name="file"
      :on-remove="handleRemove"
      :show-file-list="true"
      :headers="headers"
      :file-list="fileList"
      :on-preview="handlePictureCardPreview"
      :class="{hide: this.fileList.length >= this.limit}"
    >
      <i class="el-icon-plus"></i>
    </el-upload>

    <!-- 上传提示 -->
    <div class="el-upload__tip" slot="tip" v-if="showTip">
      请上传
      <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
      <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
      的文件
    </div>

    <el-dialog
      :visible.sync="dialogVisible"
      title="预览"
      width="800"
      append-to-body
    >
      <img
        :src="dialogImageUrl"
        style="display: block; max-width: 100%; margin: 0 auto"
      />
    </el-dialog>
  </div>
</template>

<script>
import { getToken } from "@/utils/auth";

export default {
  props: {
    value: [String, Object, Array],
    // 图片数量限制
    limit: {
      type: Number,
      default: 5,
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 5,
    },
    // 文件类型, 例如['png', 'jpg', 'jpeg']
    fileType: {
      type: Array,
      default: () => ["png", "jpg", "jpeg"],
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      dialogImageUrl: "",
      dialogVisible: false,
      hideUpload: false,
      // 本地
      // baseUrl: 'http://192.168.0.194:9000',
      // 线上
      baseUrl: process.env.VUE_APP_BASE_API,
      uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
      headers: {
        Authorization: "Bearer " + getToken(),
      },
      fileList: []
    };
  },
  watch: {
    value: {
      handler(val) {
        if (val) {
          // 首先将值转为数组
          const list = Array.isArray(val) ? val : this.value.split(',');
          // 然后将数组转为对象数组
          this.fileList = list.map(item => {
            if (typeof item === "string") {
              if (item.indexOf(this.baseUrl) === -1) {
                item = { name: this.baseUrl + item, url: this.baseUrl + item };
              } else {
                item = { name: item, url: item };
              }
            }
            return item;
          });
        } else {
          this.fileList = [];
          return [];
        }
      },
      deep: true,
      immediate: true
    }
  },
  computed: {
    // 是否显示提示
    showTip() {
      return this.isShowTip && (this.fileType || this.fileSize);
    },
  },
  methods: {
    // 删除图片
    handleRemove(file, fileList) {
      const findex = this.fileList.map(f => f.name).indexOf(file.name);
      if(findex > -1) {
        this.fileList.splice(findex, 1);
        this.$emit("input", this.listToString(this.fileList));
      }
    },
    // 上传成功回调
    handleUploadSuccess(res) {
      this.fileList.push({ name: res.fileName, url: res.fileName });
      this.$emit("input", this.listToString(this.fileList));
      this.loading.close();
    },
    // 上传前loading加载
    handleBeforeUpload(file) {
      let isImg = false;
      if (this.fileType.length) {
        let fileExtension = "";
        if (file.name.lastIndexOf(".") > -1) {
          fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
        }
        isImg = this.fileType.some(type => {
          if (file.type.indexOf(type) > -1) return true;
          if (fileExtension && fileExtension.indexOf(type) > -1) return true;
          return false;
        });
      } else {
        isImg = file.type.indexOf("image") > -1;
      }

      if (!isImg) {
        this.$message.error(
          `文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
        );
        return false;
      }
      if (this.fileSize) {
        const isLt = file.size / 1024 / 1024 < this.fileSize;
        if (!isLt) {
          this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
          return false;
        }
      }
      this.loading = this.$loading({
        lock: true,
        text: "上传中",
        background: "rgba(0, 0, 0, 0.7)",
      });
    },
    // 文件个数超出
    handleExceed() {
      this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
    },
    // 上传失败
    handleUploadError() {
      this.$message({
        type: "error",
        message: "上传失败",
      });
      this.loading.close();
    },
    // 预览
    handlePictureCardPreview(file) {
      this.dialogImageUrl = file.url;
      this.dialogVisible = true;
    },
    // 对象转成指定字符串分隔
    listToString(list, separator) {
      let strs = "";
      separator = separator || ",";
      for (let i in list) {
        strs += list[i].url.replace(this.baseUrl, "") + separator;
      }
      return strs != '' ? strs.substr(0, strs.length - 1) : '';
    }
  }
};
</script>
<style scoped lang="scss">
// .el-upload--picture-card 控制加号部分
::v-deep.hide .el-upload--picture-card {
  display: none;
}
// 去掉动画效果
::v-deep .el-list-enter-active,
::v-deep .el-list-leave-active {
  transition: all 0s;
}

::v-deep .el-list-enter, .el-list-leave-active {
  opacity: 0;
  transform: translateY(0);
}
</style>

可以在main.js中全局引入,直接在页面中使用即可,或使用时单独引入。

// 全局引入图片上传组件

import ImageUpload from "@/components/ImageUpload/index"

// 全局组件挂载

Vue.component('ImageUpload', ImageUpload)

页面中使用

<el-form-item label="卡展示图" prop="cardImg">

     <ImageUpload v-model="form.cardImg"  :limit="1"/>

</el-form-item>

2.实现手动上传

当上传文件需要与表单数据一起传送到服务器时,可设置·:auto-upload="false"进行手动上传,只有当点击确定时调用submit方法文件才会被传送。

<el-dialog
      title="数据批量导入"
      :visible.sync="upload.open"
      width="500px"
      append-to-body
    >
      <div style="padding-left: 50px">
        <el-upload
          class="upload-demo"
          drag
          :action="upload.url"
          multiple
          ref="upload"
          :headers="upload.headers"
          :disabled="upload.isUploading"
          :on-progress="handleFileUploadProgress"
          :on-success="handleFileSuccess"
          :auto-upload="false"
          accept=".xlsx, .xls"
        >
          <i class="el-icon-upload"></i>
          <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
          <!-- <div class="el-upload__text">仅支持扩展名为“.xlsx”文件上传,限2M</div> -->
          <p style="font-size:15px;cursor:pointer;color:#409eff" slot="tip" @click="downTemplateBtn">
            点击下载公司注册导入模板.xlsx
          </p>
        </el-upload>
      </div>

      <div slot="footer" class="dialog-footer">
        <el-button @click="upload.open = false">取 消</el-button>
        <el-button type="primary" @click="submitFileForm">确 定</el-button>
      </div>
    </el-dialog>

绑定数据

upload: {
        // 是否显示弹出层(用户导入)
        open: false, // 弹出层标题(用户导入)
        title: "批量导入", // 是否禁用上传
        isUploading: false, // 是否更新已经存在的用户数据
        updateSupport: 0, // 设置上传的请求头部
        headers: { Authorization: "Bearer " + getToken() }, // 上传的地址
        url: process.env.VUE_APP_BASE_API + "/businessAdmin/company/collection/import",
 },

上传到服务器

submitFileForm() {
       this.$refs.upload.submit();
 },

3.formData方式上传

form表单是比较传统的上传方法,但有一些限制,提交方式必须是post,因为get通过URL提交数据,大小一般不超过2kb,而post是没有大小限制的;enctype属性必须为"multipart/form-data"。

<form action="<%=path %>/testFormUpload" enctype="multipart/form-data" method="post" target="targetFrame">
        <input type="file" name="form_file">
        <button type="submit" >form上传</button>
    </form>

使用ajax和formData结合的方式可实现异步上传,且更具灵活性。当提交的表单中有文件时,常采用这种方式。

创建一个FormData对象
let formData = new FormData();
   将文件使用append属性追加添入
   formData.append("avatarfile", data);
        uploadAvatar(formData).then((response) => {
          this.open = false;
          this.options.img = "https://file.aqrcgj.com" + response.imgUrl;
          console.log(this.options.img);
          this.$modal.msgSuccess("修改成功");
          this.visible = false;
          this.$emit("getupdata", response.imgUrl);
        });

二.文件下载

1.二进制流格式

文件下载接口使用responseType: 'arraybuffer',返回二进制流图片数据格式。

注:当请求设置responseType: 'arraybuffer'时

请求成功时,返回文件流,正常导出文件。

请求失败时,返回的500错误信息也会被转成流。

解决方案:将已转为arraybuffer类型的数据转回Json对象,然后进行判断

async downloadFile (file) {
      let res = await this.$axios.post(this.API.order.tradeImpExcle, { responseType: "arraybuffer" });
      if (!res) return;
      try {
        //如果JSON.parse(enc.decode(new Uint8Array(res.data)))不报错,说明后台返回的是json对象,则弹框提示
        //如果JSON.parse(enc.decode(new Uint8Array(res.data)))报错,说明返回的是文件流,进入catch,下载文件
        let enc = new TextDecoder('utf-8')
        res = JSON.parse(enc.decode(new Uint8Array(res.data))) //转化成json对象
        if (res.Status == "true") {
          this.refresh()
          this.$message.success(res.Msg)
        } else {
          this.$message.error(res.Result)
        }
      } catch (err) {
        const content = res.data;
        const blob = new Blob([content]);
        let url = window.URL.createObjectURL(blob);
        let link = document.createElement("a");
        link.style.display = "none";
        link.href = url;
        link.setAttribute(
          "download",
          res.headers["content-disposition"].split("=")[1]
        );
        document.body.appendChild(link);
        link.click();
      }
    }

2.blob格式

设置请求头 responseType: ‘blob’

解决方案:将已转为blob类型的数据转回Json对象,然后进行判断

if (response.data.type==="application/json") {
            const reader=new FileReader()
            reader.readAsText(response.data, 'utf-8'); 
            reader.onload=function(){
              const{msg}=JSON.parse(reader.result)
              this.messages=msg
              _this.$message({
              message: this.messages,
              type: "error",
            });
            } 
          }

3.file-saver方法

安装:npm install file-saver --save

import { saveAs } from 'file-saver'

export function downloada(data, filename) {

  // data文件流  filename文件名加后缀

  const blob = new Blob([data])

  saveAs(blob, decodeURI(filename))

}

下载时调用方法

getFileDown(row.url).then(res=>{

    downloada(res,row.name)

})

4.生成 URL 下载方式(canvas

下载echart表图片

const downloadEchart = () => {

  // 获取画布图表地址信息

  const img = new Image();

  img.src = chart.value.getDataURL({

    type: "png",

    pixelRatio: 1,

    backgroundColor: "#fff",

  });

  // 当图片加载完成后,生成 URL 并下载

  img.onload = () => {

    const canvas = document.createElement("canvas");

    canvas.width = img.width;

    canvas.height = img.height;

    const ctx = canvas.getContext("2d");

    if (ctx) {

      ctx.drawImage(img, 0, 0, img.width, img.height);

      const link = document.createElement("a");

      link.download = `业绩柱状图.png`;

      link.href = canvas.toDataURL("image/png", 0.9);

      document.body.appendChild(link);

      link.click();

      link.remove();

    }

  };

};

三:文件预览

lookCloud(row){

      const url = this.UTILURL.YU_URL_LO_MY + row.fileAddress;

      const baseId = Base64.encode(url);

      window.open(this.UTILURL.YU_URL_JIE_MY + encodeURIComponent(baseId));

    },

this.UTILURL.YU_URL_LO_MY为文件地址前缀,this.UTILURL.YU_URL_JIE_MY 为文件服务器地址,以此访问图片。

注:使用window.open(URL,name,specs,replace)实现,用于打开一个新的浏览器窗口

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值