前端实现压缩图片的功能(vue-element)

news2024/11/17 9:32:50

前言:

        随着现在手机像素,拍照功能越来越好,随之而来的是本地图片越来越大,那么如何更好的将本地图片上传到后端接口呢?这是后台管理系统常见的场景和头疼的问题,这里分享下个人的方法。

实现效果:

如下图所示,从 580kb ->  压缩后  150kb

 实现步骤:

1、父级引入封装文件

//页面上
<el-dialog
      title="压缩图片"
      :visible.sync="compressImgVisible"
      width="700"
      foot-hide
    >
      <uploadImg
      />
    </el-dialog>

//js中
data() {
    return {
      compressImgVisible: false,
    }

2、首先实现上传功能,使用el-upload,

封装uploadImg.vue

<template>
  <div class="uploadImgBody">
    <!--上传图片部分-->
    <el-upload
      class="upload-image"
      ref="upload"
      :action="action"
      :headers="headers"
      :multiple="multiple"
      :data="data"
      :name="name"
      :show-file-list="showFileList"
      :drag="drag"
      :accept="accept"
      :list-type="listType"
      :auto-upload="autoUpload"
      :disabled="is_disabled"

      :before-upload="beforeUpload"
    >
      <!--弹框展示上传以后的图片-->
      <img
        class="fileImg"
        v-if="mrImgUrl"
        :src="mrImgUrl"
      >
      <div v-else>
        <i class="el-icon-plus"></i>
      </div>
    </el-upload>
  </div>

</template>

<script>
//element的上传图片,压缩图片组件
export default {
  props:{
    /**
     * 自动上传参数
     * */
    autoUpload:{ // 是否需要选取完自动上传功能
      type: Boolean,
      default: true
    },
    // 默认图片,父级传过来 http开头的文件
    mrImgUrl:{
      type: String,
      default: ''
    },
    action:{//上传的地址
      type: String,
      default: ''
    },
    headers: {//设置上传的请求头部
      type:Object,
      default: () => {
        return {}
      }
    },
    data: {//上传时额外带的参数
      type:Object,
      default: () => {
        return {}
      }
    },
    name:{//上传的文件字段名
      type: String,
      default: 'file'
    },
    cookieOK:{//支持发送 cookie 凭证信息
      type: Boolean,
      default: true
    },
    /**
     * 公共参数
     * */
    showFileList:{//是否显示已上传文件列表
      type: Boolean,
      default: false
    },
    drag:{//是否启用拖拽上传
      type: Boolean,
      default: false
    },
    accept:{//接受文件类型-图片上传类型-不同的格式之间以逗号隔开
      type: String,
      default: '.jpg,.jpeg,.png'
    },
    listType:{ // 文件列表的类型 - text/picture/picture-card
      type: String,
      default: 'picture-card'
    },
    fileList:{//已上传的文件列表,
      type:Array,
      default: () => {
        return [
          {
            name: 'food.jpeg',
            url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'
          }
        ]
      }
    },
    is_disabled:{//是否禁止,true是禁止,false不禁止
      type: Boolean,
      default: false
    },
    multiple:{//是否可以多选
      type: Boolean,
      default: true
    },

  },
  watch: {},
  data() {
    return {

    }
  },
  methods: {
    /**
     * @上传文件之前的钩子
     * @params file  图片的file文件
     * @return uploadFile 把文件发送给父级
     * @tip 多选会调用多次该方法
     */
    beforeUpload(file) {
      this.$emit('uploadFile',file);
      return
    }

  },

}
</script>

<style lang='scss' scoped>
.uploadImgBody{
  height: auto;
  .upload-image{
    width:200px;
    height: 200px;
    .fileImg{
      width:100%;
      height: 100%;
    }
  }
  .showImg{
    width:100px;
    height: 100px;
  }

}

</style>

3、加入压缩功能

逻辑:

        首先,把file文件转成  canvas图片,然后canvas压缩图片利用canvas.toDataURL()将canvas绘制的图像转成图片从而达到压缩图片尺寸的效果

HTMLCanvasElement.toDataURL()

 具体方法:其中 dataUrl 就是拿到的canvas图片的base64地址

 /**
     * @压缩公共方法
     * @params file
     * @return 压缩后的文件,支持两种,file和 blob
     */
    compressImg(file) {
      const reader = new FileReader();
      // readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend (en-US) 事件,
      // 同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。
      reader.readAsDataURL(file);
      reader.onload = () => {
        const img = new Image();
        img.src = reader.result;
        img.onload = () => {
          // 图片的宽高
          const w = img.width;
          const h = img.height;
          const canvas = document.createElement("canvas");
          // canvas对图片进行裁剪,这里设置为图片的原始尺寸
          canvas.width = w;
          canvas.height = h;
          const ctx = canvas.getContext("2d");
          // canvas中,png转jpg会变黑底,所以先给canvas铺一张白底
          ctx.fillStyle = "#fff";
          // fillRect()方法绘制一个填充了内容的矩形,这个矩形的开始点(左上点)在
          // (x, y) ,它的宽度和高度分别由width 和 height 确定,填充样式由当前的fillStyle 决定。
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          // 绘制图像
          ctx.drawImage(img, 0, 0, w, h);

          // canvas转图片达到图片压缩效果
          // 返回一个包含图片展示的 data URI base64 在指定图片格式为 image/jpeg 或 image/webp的情况下,
          // 可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。
          const dataUrl = canvas.toDataURL("image/jpeg", 0.8);
          this.dialogImageUrl = dataUrl

        };
      };
    },

4、拿到的base64地址,不能直接给后端,要转格式,这里提供两种,一是file文件,跟压缩前的格式一样,还有一种是blob方法

// canvas生成的格式为base64,需要进行转化, base64->file
    dataURLtoFile(dataurl,fileName) {
      let arr = dataurl.split(','),
      mime = arr[0].match(/:(.*?);/)[1],

      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], fileName, {type:mime})
    },
    // canvas生成的格式为base64,需要进行转化, base64->blob
    dataURLtoBlob(dataurl) {
      const arr = dataurl.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]);
      let n = bstr.length;
      const u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], { type: mime });
    },

实现源码(上传+压缩):

uploadImg.vue

<template>
  <div class="uploadImgBody">
    <!--上传图片部分-->
    <el-upload
      class="upload-image"
      ref="upload"
      :action="action"
      :headers="headers"
      :multiple="multiple"
      :data="data"
      :name="name"
      :show-file-list="showFileList"
      :drag="drag"
      :accept="accept"
      :list-type="listType"
      :auto-upload="autoUpload"
      :disabled="is_disabled"

      :before-upload="beforeUpload"
    >
      <!--弹框展示上传以后的图片-->
      <img
        class="fileImg"
        v-if="mrImgUrl"
        :src="mrImgUrl"
      >
      <div v-else>
        <i class="el-icon-plus"></i>
      </div>
    </el-upload>
    源图片大小:<span v-if="sourceFile.size">{{ sourceFile.size/1024 }}</span> kb
    <el-button @click="compressImgFun" style="display: block;margin:10px 0;">点我压缩</el-button>
    压缩图片大小:<span v-if="compressFile.size">{{compressFile.size/1024 }}</span> kb
    <img
      v-if="dialogImageUrl"
      class="showImg"
      :src="dialogImageUrl"
    >
  </div>

</template>

<script>
//element的上传图片,压缩图片组件
export default {
  props:{
    /**
     * 自动上传参数
     * */
    autoUpload:{ // 是否需要选取完自动上传功能
      type: Boolean,
      default: true
    },
    // 默认图片,父级传过来 http开头的文件
    mrImgUrl:{
      type: String,
      default: ''
    },
    action:{//上传的地址
      type: String,
      default: ''
    },
    headers: {//设置上传的请求头部
      type:Object,
      default: () => {
        return {}
      }
    },
    data: {//上传时额外带的参数
      type:Object,
      default: () => {
        return {}
      }
    },
    name:{//上传的文件字段名
      type: String,
      default: 'file'
    },
    cookieOK:{//支持发送 cookie 凭证信息
      type: Boolean,
      default: true
    },
    /**
     * 公共参数
     * */
    showFileList:{//是否显示已上传文件列表
      type: Boolean,
      default: false
    },
    drag:{//是否启用拖拽上传
      type: Boolean,
      default: false
    },
    accept:{//接受文件类型-图片上传类型-不同的格式之间以逗号隔开
      type: String,
      default: '.jpg,.jpeg,.png'
    },
    listType:{ // 文件列表的类型 - text/picture/picture-card
      type: String,
      default: 'picture-card'
    },
    fileList:{//已上传的文件列表,
      type:Array,
      default: () => {
        return [
          {
            name: 'food.jpeg',
            url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'
          }
        ]
      }
    },
    is_disabled:{//是否禁止,true是禁止,false不禁止
      type: Boolean,
      default: false
    },
    multiple:{//是否可以多选
      type: Boolean,
      default: true
    },

  },
  watch: {},
  data() {
    return {
      dialogImageUrl:'',//源图片
      sourceFile:{}, //上传后的图片
      compressFile:{} //压缩后的图片

    }
  },
  methods: {
    /**
     * @上传文件之前的钩子
     * @params file  图片的file文件
     * @return uploadFile 把文件发送给父级
     * @tip 多选会调用多次该方法
     */
    beforeUpload(file) {
      this.sourceFile = file
      this.$emit('uploadFile',file);
      return
    },
    compressImgFun(){
      this.compressImg(this.sourceFile)
      console.log('压缩前的图片文件:file');
      console.log(this.sourceFile);
    },
    /**
     * @压缩公共方法
     * @params file
     * @return 压缩后的文件,支持两种,file和 blob
     */
    compressImg(file) {
      const reader = new FileReader();
      // readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend (en-US) 事件,
      // 同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。
      reader.readAsDataURL(file);
      reader.onload = () => {
        const img = new Image();
        img.src = reader.result;
        img.onload = () => {
          // 图片的宽高
          const w = img.width;
          const h = img.height;
          const canvas = document.createElement("canvas");
          // canvas对图片进行裁剪,这里设置为图片的原始尺寸
          canvas.width = w;
          canvas.height = h;
          const ctx = canvas.getContext("2d");
          // canvas中,png转jpg会变黑底,所以先给canvas铺一张白底
          ctx.fillStyle = "#fff";
          // fillRect()方法绘制一个填充了内容的矩形,这个矩形的开始点(左上点)在
          // (x, y) ,它的宽度和高度分别由width 和 height 确定,填充样式由当前的fillStyle 决定。
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          // 绘制图像
          ctx.drawImage(img, 0, 0, w, h);

          // canvas转图片达到图片压缩效果
          // 返回一个包含图片展示的 data URI base64 在指定图片格式为 image/jpeg 或 image/webp的情况下,
          // 可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。
          const dataUrl = canvas.toDataURL("image/jpeg", 0.8);
          this.dialogImageUrl = dataUrl

          // base64格式文件转成Blob文件格式
          let blobFile = this.dataURLtoBlob(dataUrl);
          console.log("压缩后的图片:Blob文件----------");
          console.log(blobFile);
          // base64格式文件转成file文件格式
          let fileName = this.sourceFile.name
          let fileImg = this.dataURLtoFile(dataUrl,fileName);
          console.log("压缩后的图片:file文件----------");
          console.log(fileImg);
          this.compressFile = fileImg

        };
      };
    },
    // canvas生成的格式为base64,需要进行转化, base64->file
    dataURLtoFile(dataurl,fileName) {
      let arr = dataurl.split(','),
      mime = arr[0].match(/:(.*?);/)[1],

      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], fileName, {type:mime})
    },
    // canvas生成的格式为base64,需要进行转化, base64->blob
    dataURLtoBlob(dataurl) {
      const arr = dataurl.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]);
      let n = bstr.length;
      const u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], { type: mime });
    },


  },

}
</script>

<style lang='scss' scoped>
.uploadImgBody{
  height: auto;
  .upload-image{
    width:200px;
    height: 200px;
    .fileImg{
      width:100%;
      height: 100%;
    }
  }
  .showImg{
    width:100px;
    height: 100px;
  }

}

</style>

更多资料:

前端压缩图片上传_泡泡大怪兽的博客-CSDN博客

前端图片压缩(几乎无损)_蓝格子.的博客-CSDN博客_前端无损压缩

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375853.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java多线程(八)线程等待与线程唤醒2

三、wait(long timeout)和notify() wait(long timeout)会让当前线程处于“等待(阻塞)状态”&#xff0c;“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法&#xff0c;或者超过指定的时间量”&#xff0c;当前线程被唤醒(进入“就绪状态”)。 下面的示例就是演示…

入职数据分析公认的好书|建议收藏

众所周知&#xff0c;数据分析经常出现在我们的日常生活中&#xff0c;各行各业都需要数据分析。可你知道什么是数据分析&#xff1f;它在企业里到底扮演什么角色&#xff1f;以及如果我们自己也想拥有数据分析的能力&#xff0c;以便更好的满足数据分析的需求&#xff0c;我们…

系列六、存储引擎

一、存储引擎介绍 大家可能没有听说过存储引擎&#xff0c;但是一定听过引擎这个词&#xff0c;引擎就是发动机&#xff0c;是一个机器的核心组件。 比如&#xff0c;对于舰载机、直升机、火箭来说&#xff0c;他们都有各自的引擎&#xff0c;是他们最为核心的组件。而我们在选…

vue脚手架多页自动化生成实践

前言 在前端开发过程中&#xff0c;常常面对多种业务场景。到目前为止&#xff0c;前端对于不同场景的处理通常会采用不同的渲染方案来组合处理&#xff0c;常见的渲染方案包括&#xff1a;CSR(Client Side Rendering)、SSR(Server Side Rendering)、SSG(Static Site Generati…

2023年房地产行业研究报告

第一章 行业发展概况 房地产业是指以土地和建筑物为经营对象&#xff0c;从事房地产开发、建设、经营、管理以及维修、装饰和服务的集多种经济活动为一体的综合性产业&#xff0c;是具有先导性、基础性、带动性和风险性的产业。主要包括&#xff1a;土地开发&#xff0c;房屋的…

Vue3 -- PDF展示、添加签名(带笔锋)、导出

文章目录笔锋签名方案一实现要点实现过程组件引用页面元素添加引用实现代码效果展示缺点方案二修改页面元素替换引用修改代码效果展示完整代码地址实现功能的时候采用了两个方案&#xff0c;主要是第一个方案最后的实现效果并不太理想&#xff0c;但实现起来比较简单&#xff0…

JavaScript手写题

一、防抖 function debounce(fn, delay200) {let timeout null; // 定时器控制return function(...args) {if (timeout) { // 定时器存在&#xff0c;表示某个动作之前触发过了clearTimeout(timeout); // 清除定时器timeout null;} else {// 对第一次输入立即执行fn.apply…

【Leetcode 剑指Offer】第 5 天 查找算法(中等)

查找算法剑指 Offer 04. 二维数组中的查找剑指 Offer 11. 旋转数组的最小数字剑指 Offer 50. 第一个只出现一次的字符Python字典基础哈希表&#xff08;python中是dict()&#xff09;有序哈希表第一个中等&#xff0c;后两个简单题。剑指 Offer 04. 二维数组中的查找 题&#…

Node.js的研究和应用

首先我们要知道什么是node.js? Node.js采用谷歌的V8引擎&#xff0c;是一个服务器端的、非阻断式I/O的、事件驱动的 JavaScript运行环境。 一、Node.js架构具体包含什么呢&#xff1f;咱们从一个图来看看会更加明白。 natives modules&#xff1a; 1.当前层内容由js实现 。…

不使用implements关键字实现实现类(类似于mapper)

首先&#xff0c;说明一下功能需求&#xff0c;平时定义一个接口&#xff0c;就要使用implements关键字来实现接口。那么&#xff0c;当不使用此关键字的时候&#xff0c;是否也能使相关接口也能够绑定实现类呢&#xff1f; 答案是肯定的。 此篇文章的主要功能有两个&#xf…

知识类问答数据集资源对外开放:百万级百度知道、社区问答及六大领域级小规模语料概述

随着chatgpt的火热&#xff0c;中文知识类问答数据集由收到诸多关注&#xff0c;其作为高质量的QA数据&#xff0c;可以用于SFT阶段以及pretrain预训练阶段。 本文主要介绍目前开源可下载的两个较大规模的知识类数据集&#xff0c;包括147万百度知道知识类数据集、425万社区问…

IOS逆向前期环境准备笔记

ios系统由于效验问题&#xff0c;只能升级不能降级&#xff0c;需要特别注意&#xff0c; 刷系统可以在爱思上搞定&#xff1b; 越狱推荐使用u盘镜像及本地启动盘制作&#xff1a; 注意&#xff0c;要进去bios,关闭安全启动&#xff0c;不然直接失败&#xff1a; Checkra1n镜…

linux升级gcc版本详细教程

0.前言一般linux操作系统默认的gcc版本都比较低&#xff0c;例如centos7系统默认的gcc版本为4.8.5。gcc是从4.7版本开始支持C11的&#xff0c;4.8版本对C11新特性的编译支持还不够完善&#xff0c;因此如果需要更好的体验C11以及以上版本的新特性&#xff0c;需要升级gcc到一个…

九、Vben之可拖拽穿梭框和水印背景如何添加

近期在开发的过程中遇到了两个难题&#xff0c;一个是目前的穿梭框不支持产品的要求&#xff0c;不能够上下拖拽&#xff0c;二是vben没有水印的api&#xff0c;需要我们自己来开发。 一、可拖拽穿梭框 做成功的效果如下&#xff1a; 思路&#xff1a; 将table放入transfer的…

操作系统权限提升(十七)之绕过UAC提权-Windows令牌概述和令牌窃取攻击

系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权 操作系统权限提升(十四)之绕过UAC提权-基于白名单AutoElevate绕过UAC提权 操作系统权限提升(十五)之绕过UAC提权-基于白名单DLL劫持绕过UAC提权 操作系…

android EditText设置后缀

有两种实现方案。 方案一&#xff1a;是自己写一个TextWatcher。 方案二&#xff1a;是重写TextView的getOffsetForPosition方法&#xff0c;返回一个计算好的offset。 我在工作时&#xff0c;使用的是方案一。在离职之后&#xff0c;我还是对这个问题耿耿于怀&#xff0c;所以…

git在工作中的正常使用

开发A和B功能后进行发版。。 一、拉取代码 git clone http://ntc.ntsvars.com:8090/lvweijie/test.git二、开发功能A任务 创建A任务本地分支 #创建A分支&#xff0c;并切换A分支 git checkout -b A三、开发A任务 四、提交A功能文件到本地分支 git add .五、添加提交A功能备…

Tina_Linux打包流程说明指南_new

OpenRemoved_Tina_Linux_打包流程_说明指南_new 1 概述 1.1 编写目的 介绍Allwinner 平台上打包流程。 1.2 适用范围 Allwinner 软件平台Tina v3.0 版本以上。 1.3 相关人员 适用Tina 平台的广大客户&#xff0c;想了解Tina 打包流程的开发人员。 2 固件打包简介 固件…

Jenkins+Gitlab实现代码自动构建部署

一、环境准备 主机名ip安装软件jenkins192.168.75.149jenkinsgitlab192.168.75.147gitlabweb192.168.75.155部署应用 二、jenkins服务器配置 1、生产公钥 [rootjenkins ~]# ssh-keygen &#xff08;2&#xff09;获取公钥信息 公钥信息在配置 Gitlab SSH Keys 时用到。 &am…

Tcpdump抓包验证zookeeper的心跳机制

一、背景 在分布式系统中&#xff0c;zookeeper可以作为服务注册中心&#xff0c;所有提供服务的节点都可以在zookeeper上面注册&#xff0c;并作为一个node被组织起来&#xff0c;如下图&#xff1a; 在RPC框架中&#xff0c;这些服务提供者就是RPC服务的提供者。zookeeper注…