vue + el-upload 实现图片尺寸缩小

一、背景

前端对图片进行 剪裁 或者 大小进行压缩 都是比较场景的需求。但是这次接到一个 将图片尺寸缩小 的需求,为啥呢?因为在前端上传到服务器后,有个功能会导出 word,而图片就会放到里面去导出,而图片尺寸太大,显示会有问题;例如简历个人信息里面的身份证、学历、头像等照片(这种问题其实我觉得应该由后端处理,但既然知道了,就研究研究)。

二、实现思路

有个问题需要先确定:缩放实现在上传前还是上传后,区别是什么?
el-upload组件中一般都有大图预览功能,即el-image 中的预览功能previewSrcList

  • 上传前缩小:保存后的图片较小,预览大图也不能看到原始尺寸(没保存前可以看到)
  • 上传后缩小:需要上传两张图,并且小图需要做无感上传,原始图和缩小后的图片,并且需要定义区分规则,怎么获取大图,怎么获取小图

由于项目中对大图展示要求不高,故采用 上传前缩小 实现;

三、代码实现

el-upload 二次封装代码不尽相同,故在此只展现压缩相关核心代码

compressImgSize(file) { // 压缩图片
  // file 里面没有url 字段(el-upload自带,用于图片展示的临时路径),则通过URL.createObjectURL(file)转化为临时路径
  // const fileUrl = URL.createObjectURL(file)
  // const image = new Image();
  // image.src = fileUrl;

  const image = new Image();
  image.src = file.url;
  image.onload = (e) => { // onload 才能获取到图片真实的宽高
    console.log('image.width', image, image.width, image.height)

    if (image.width > 0 && image.height > 0) {
      const oldScale = Number((image.width / image.height).toFixed(2));

      let maxWidth = 200, maxHeight = 200; // 定义缩放后的最大宽高
      let targetWidth = image.width, targetHeight = image.height; // 图片缩放后的真实宽高, 默认为图片的原始尺寸
      if (targetWidth > maxWidth) { // 目标宽度大于最大宽度
        targetWidth = maxWidth;
        targetHeight = targetWidth / oldScale;
      }
      //缩放后高度仍然大于最大高度继续按比例缩小
      if (targetHeight > maxHeight) {
        targetHeight = maxHeight
        targetWidth = targetHeight * oldScale;
      }

      const canvas = document.createElement('canvas'); // 创建一个canvas节点
      const context = canvas.getContext('2d');
      canvas.width = targetWidth; // 设置canvas的宽高
      canvas.height = targetHeight;
      context.drawImage(image, 0, 0, targetWidth, targetHeight); // 将图片绘制到canvas内部
      const imageBase64 = canvas.toDataURL('image', 0.92);//canvas导出成为base64
      this.upData.file = this.base64ToFile(imageBase64, 'file') // 将base64图片转为 file 对象

      this.$refs.upload.submit(); // 触发上传
    }
  }
  

},

// base64图片转file的方法(base64图片, 设置生成file的文件名)
base64ToFile(base64, fileName) {
  // 将base64按照 , 进行分割 将前缀  与后续内容分隔开
  let data = base64.split(',');
  // 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
  let type = data[0].match(/:(.*?);/)[1];
  // 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
  let suffix = type.split('/')[1];
  // 使用atob()对base64数据进行解码  结果是一个文件数据流 以字符串的格式输出
  const bstr = window.atob(data[1]);
  // 获取解码结果字符串的长度
  let n = bstr.length
  // 根据解码结果字符串的长度创建一个等长的整形数字数组
  // 但在创建时 所有元素初始值都为 0
  const u8arr = new Uint8Array(n)
  // 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
  while (n--) {
    // charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
    u8arr[n] = bstr.charCodeAt(n)
  }
  // 利用构造函数创建File文件对象
  // new File(bits, name, options)
  const file = new File([u8arr], `${fileName}.${suffix}`, {
    type: type
  })
  // 将File文件对象返回给方法的调用者
  return file;
}

注意点:

  1. compressImgSize 压缩方法调用位置在 上传文件之前的钩子 before-upload 或者是 文件状态改变时的钩子 on-change
  2. 上传文件之前的钩子 before-upload 拿到的 file 没有url,需要通过 URL.createObjectURL(file) 转化
  3. 文件状态改变时的钩子 on-change 里面有临时路径 url,可直接使用
  4. this.$refs.upload.submit() 手动触发的上传方法,文件默认还是原始图片,需要通过配置 data: { file: this.upData.fil } => 上传时附带的额外参数 实现上传缩小后的图片;但el-upload默认在上传时携带了file参数并不可删除(但是可以用过配置name: "oldFile"去修改,保证后端不再使用到,查阅el-upload源码可知,文档没有),所以可以通过配置 name="file2" 将原本的file => 修改成file2 即可。

ps:
最佳方式还是由后端重新写一个 接口A,在导出接口中调用 接口A 时,将图片临时处理缩小后返回即可,这种操作不能影响到前端大图展示
 

文章仅为本人学习过程的一个记录,仅供参考,如有问题,欢迎指出!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现Vue + Axios + El-upload进行文件上传并跨域,需要按照以下步骤进行操作: 1. 在Vue项目中安装axios和element-ui。在命令行中输入以下命令即可安装: ``` npm install axios element-ui --save ``` 2. 在Vue项目中创建一个上传文件的组件,例如FileUpload.vue。在该组件中导入axios和element-ui,并且引入El-upload组件。示例代码如下: ```vue <template> <div> <el-upload class="upload-demo" :action="uploadUrl" :on-success="handleUploadSuccess" :before-upload="beforeUpload" :headers="headers" :data="formData" :file-list="fileList"> <el-button size="small" type="primary">点击上传</el-button> </el-upload> </div> </template> <script> import axios from 'axios' import { Message } from 'element-ui' export default { name: 'FileUpload', data () { return { fileList: [], uploadUrl: 'http://example.com/upload', formData: {}, headers: { 'Content-Type': 'multipart/form-data' } } }, methods: { handleUploadSuccess (response, file, fileList) { // 上传成功后的处理逻辑 console.log(response) }, beforeUpload (file) { // 文件上传前的处理逻辑 console.log(file) } } } </script> ``` 3. 在组件中实现文件上传的逻辑。在上传文件之前,需要设置请求头和请求数据,并且需要处理跨域请求。可以在组件的methods中定义一个upload方法,用来发送上传请求。示例代码如下: ```vue <script> import axios from 'axios' import { Message } from 'element-ui' export default { name: 'FileUpload', data () { return { fileList: [], uploadUrl: 'http://example.com/upload', formData: {}, headers: { 'Content-Type': 'multipart/form-data' } } }, methods: { handleUploadSuccess (response, file, fileList) { // 上传成功后的处理逻辑 console.log(response) }, beforeUpload (file) { // 文件上传前的处理逻辑 console.log(file) }, upload () { let config = { headers: this.headers, withCredentials: true // 跨域请求需要设置withCredentials为true } let data = new FormData() data.append('file', this.fileList[0].raw) // 向后端发送上传请求 axios.post(this.uploadUrl, data, config) .then(response => { this.handleUploadSuccess(response) }) .catch(error => { console.log(error) }) } } } </script> ``` 4. 最后,在Vue组件中使用El-upload组件,并且调用upload方法即可实现文件上传。示例代码如下: ```vue <template> <div> <el-upload class="upload-demo" :action="uploadUrl" :on-success="handleUploadSuccess" :before-upload="beforeUpload" :headers="headers" :data="formData" :file-list="fileList"> <el-button size="small" type="primary" @click="upload">点击上传</el-button> </el-upload> </div> </template> <script> import axios from 'axios' import { Message } from 'element-ui' export default { name: 'FileUpload', data () { return { fileList: [], uploadUrl: 'http://example.com/upload', formData: {}, headers: { 'Content-Type': 'multipart/form-data' } } }, methods: { handleUploadSuccess (response, file, fileList) { // 上传成功后的处理逻辑 console.log(response) }, beforeUpload (file) { // 文件上传前的处理逻辑 console.log(file) }, upload () { let config = { headers: this.headers, withCredentials: true // 跨域请求需要设置withCredentials为true } let data = new FormData() data.append('file', this.fileList[0].raw) // 向后端发送上传请求 axios.post(this.uploadUrl, data, config) .then(response => { this.handleUploadSuccess(response) }) .catch(error => { console.log(error) }) } } } </script> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值