手把手教你实现一个图片压缩工具(Vue与Node的完美配合)

前言

图片压缩对于我们日常生活来讲,是非常实用的一项功能。有时我们会在在线图片压缩网站上进行压缩,有时会在电脑下软件进行压缩。那么我们能不能用前端的知识来自己实现一个图片压缩工具呢?答案是有的。

效果展示

原图片大小:82KB压缩后的图片大小:17KB

测试

是不是特别good!!!看到上面的压缩后的图片,可能你还会质疑图片的清晰度,那么看下面(第一张图为压缩后的图片):

教程

这么好的工具,那我们来看看怎么用代码实现它。首先你可能需要一些Vue.js和Node.js的基础,另外你可能还需要一点对知识的渴望~ 哈哈哈。

话不多说,我们来上干货。

前台搭建

1<template> 2  <div class="face"> 3    <label for="file" class="inputlabelBox"> 4      <input 5        type="file" 6        ref="pic" 7        id="file" 8        name="face" 9        accept="image/*" 10        capture="camera" 11        :style="{ display: 'none' }" 12        @change="handleClick" 13      /> 14      <div class="upload">上传图片</div> 15    </label> 16    <div class="imgbox" v-show="imgsrc != ''"> 17      <img src id="imgs" alt /> 18    </div> 19    <div> 20      <class="upload" @click="keepImg" v-show="imgsrc != ''">确定</p> 21    </div> 22  </div> 23</template> 24<script> 25import EXIF from "exif-js"; 26export default { 27  name: "imgzip", 28  data() { 29    return { 30      imgsrc: "", 31    }; 32  }, 33  methods: { 34    // 上传图片 35    handleClick() { 36      if (this.$refs.pic.files[0]) { 37 38        // this.fileToBase64(this.$refs.pic.files[0]).then((res) => { 39        //   this.imgsrc = res; 40        // }); 41 42        this.rotateImg(this.$refs.pic.files[0]).then((res) => { 43          this.imgsrc = res; 44        }); 45      } 46    }, 47    // 压缩和图片旋转 48    rotateImg(imgFile) { 49      return new Promise((resolve) => { 50        EXIF.getData(imgFile, function () { 51          let exifTags = EXIF.getAllTags(this); 52          let reader = new FileReader(); 53          reader.readAsDataURL(imgFile); 54          reader.onload = (e) => { 55            let imgData = e.target.result; 56            document.querySelector("#imgs").src = e.target.result; 57            // 8 表示 顺时针转了90 58            // 3 表示 转了 180 59            // 6 表示 逆时针转了90 60            if ( 61              exifTags.Orientation == 8 || 62              exifTags.Orientation == 3 || 63              exifTags.Orientation == 6 64            ) { 65              //翻转 66              //获取原始图片大小 67              const img = new Image(); 68              img.src = imgData; 69              img.onload = function () { 70                let cvs = document.createElement("canvas"); 71                let ctx = cvs.getContext("2d"); 72                //如果旋转90 73                if (exifTags.Orientation == 8 || exifTags.Orientation == 6) { 74                  cvs.width = img.height; 75                  cvs.height = img.width; 76                } else { 77                  cvs.width = img.width; 78                  cvs.height = img.height; 79                } 80                if (exifTags.Orientation == 6) { 81                  //原图逆时针转了90, 所以要顺时针旋转90 82                  ctx.rotate((Math.PI / 180) * 90); 83                  ctx.drawImage( 84                    img, 85                    0, 86                    0, 87                    img.width, 88                    img.height, 89                    0, 90                    -img.height, 91                    img.width, 92                    img.height 93                  ); 94                } 95                if (exifTags.Orientation == 3) { 96                  //原图逆时针转了180, 所以顺时针旋转180 97                  ctx.rotate((Math.PI / 180) * 180); 98                  ctx.drawImage( 99                    img, 100                    0, 101                    0, 102                    img.width, 103                    img.height, 104                    -img.width, 105                    -img.height, 106                    img.width, 107                    img.height 108                  ); 109                } 110                if (exifTags.Orientation == 8) { 111                  //原图顺时针旋转了90, 所以要你时针旋转90 112                  ctx.rotate((Math.PI / 180) * -90); 113                  ctx.drawImage( 114                    img, 115                    0, 116                    0, 117                    img.width, 118                    img.height, 119                    -img.width, 120                    0, 121                    img.width, 122                    img.height 123                  ); 124                } 125                let data = cvs.toDataURL("image/jpeg"); // 输出压缩后的base64 126                let arr = data.split(","), 127                  mime = arr[0].match(/:(.*?);/)[1], // 转成blob 128                  bstr = atob(arr[1]), 129                  n = bstr.length, 130                  u8arr = new Uint8Array(n); 131                while (n--) { 132                  u8arr[n] = bstr.charCodeAt(n); 133                } 134                let files = new window.File( 135                  [new Blob([u8arr], { type: mime })], 136                  "test.jpeg", 137                  { type: "image/jpeg" } 138                ); 139                resolve(files); 140              }; 141            } else { 142              let image = new Image(); //新建一个img标签(还没嵌入DOM节点) 143              image.src = e.target.result; 144              image.onload = function () { 145                let canvas = document.createElement("canvas"), // 新建canvas 146                  context = canvas.getContext("2d"), 147                  imageWidth = image.width, //压缩后图片的大小 148                  imageHeight = image.height, 149                  data = ""; 150                canvas.width = imageWidth; 151                canvas.height = imageHeight; 152                context.drawImage(image, 0, 0, imageWidth, imageHeight); 153                data = canvas.toDataURL("image/jpeg"); // 输出压缩后的base64 154                let arr = data.split(","), 155                  mime = arr[0].match(/:(.*?);/)[1], // 转成blob 156                  bstr = atob(arr[1]), 157                  n = bstr.length, 158                  u8arr = new Uint8Array(n); 159                while (n--) { 160                  u8arr[n] = bstr.charCodeAt(n); 161                } 162                let files = new window.File( 163                  [new Blob([u8arr], { type: mime })], 164                  "test.jpeg", 165                  { type: "image/jpeg" } 166                ); // 转成file 167                resolve(files); 168              }; 169            } 170          }; 171        }); 172      }); 173    }, 174 175    /* 176    fileToBase64(file) { 177      let that = this, 178        reader = new FileReader(); 179      reader.readAsDataURL(file); 180      return new Promise((resolve, reject) => { 181        reader.onload = function (e) { 182          //这里是一个异步,所以获取数据不好获取在实际项目中,就用new Promise解决 183          if (this.result) { 184            let image = new Image(); //新建一个img标签(还没嵌入DOM节点) 185            image.src = e.target.result; 186            document.querySelector("#imgs").src = e.target.result; 187            image.onload = function () { 188              let canvas = document.createElement("canvas"), // 新建canvas 189                context = canvas.getContext("2d"), 190                imageWidth = image.width / 2, //压缩后图片的大小 191                imageHeight = image.height / 2, 192                data = ""; 193              canvas.width = imageWidth; 194              canvas.height = imageHeight; 195              context.drawImage(image, 0, 0, imageWidth, imageHeight); 196              data = canvas.toDataURL("image/jpeg"); // 输出压缩后的base64 197              let arr = data.split(","), 198                mime = arr[0].match(/:(.*?);/)[1], // 转成blob 199                bstr = atob(arr[1]), 200                n = bstr.length, 201                u8arr = new Uint8Array(n); 202              while (n--) { 203                u8arr[n] = bstr.charCodeAt(n); 204              } 205              let files = new window.File( 206                [new Blob([u8arr], { type: mime })], 207                "test.jpeg", 208                { type: "image/jpeg" } 209              ); // 转成file 210              resolve(files); 211            }; 212          } else { 213            reject("err"); 214          } 215        }; 216      }); 217    }, 218    */ 219 220 221    // 保存图片 222    keepImg() { 223      // this.$emit("canvasToImage", this.imgsrc); 224 225      const fd = new FormData(); 226      fd.append("file", this.imgsrc); 227      fetch("http://localhost:6300/upload", { 228        method: "post", 229        mode:"cors", 230        body:fd, 231      }) 232        .then((response) => response.json()) 233        .then((response) => { 234          if(response.success){ 235            console.log(this.imgsrc); 236            const size = this.imgsrc.size<1024?this.imgsrc.size+"字节":Math.round(this.imgsrc.size/1024)+"KB"; 237            console.log(size); 238            alert(`图片${response.name}${response.msg}!压缩后图片大小为:${size}`); 239          } 240        }) 241        .catch((err) => { 242          console.log(err); 243        }); 244    }, 245  }, 246}; 247</script> 248<style scoped lang="less"> 249.upload { 250  display: inline-block; 251  background: #ffb90f; 252  color: white; 253  font-size: 16px; 254  text-align: center; 255  border-radius: 4px; 256  padding: 10px 30px; 257  margin-bottom: 20px; 258} 259.upload:hover { 260  filter: brightness(110%); 261} 262.upload:active { 263  filter: brightness(60%); 264} 265.imgbox { 266  text-align: center; 267  width: 60%; 268  margin: 0 auto; 269  img { 270    width: 100%; 271    height: 60vh; 272    object-fit: contain; 273  } 274} 275.face { 276  margin-top: 30px; 277  .container1 { 278    background: #000; 279    position: relative; 280    width: 580px; 281    height: 436px; 282    margin: 0 auto; 283    #canvas1 { 284      position: absolute; 285    } 286    video, 287    #canvas, 288    #canvas1 { 289      position: absolute; 290      top: 0; 291      left: 0; 292      right: 0; 293      bottom: 0; 294      width: 581px; 295      height: 436px; 296    } 297  } 298  .btns { 299    padding: 10px; 300    button { 301      margin: 20px 20px 20px 0; 302    } 303  } 304  .tips { 305    font-size: 26px; 306    color: #666; 307    margin: 10px 0; 308    line-height: 48px; 309  } 310  .imgs { 311    p { 312      font-size: 28px; 313    } 314  } 315} 316</style> 317

我在这里实现了一个Vue组件(所以你得知道Vue是什么?组件又是什么?)。知道这些还不够,你还要知道怎么从依赖库下载依赖,这里需要另外下载的依赖是exif-js

一个JavaScript库,用于从图像文件中读取EXIF元数据。您可以通过图像或文件输入元素在浏览器中的图像上使用它。EXIF和IPTC元数据均被检索。该软件包还可以在AMD或CommonJS环境中使用。

备注;使用exif.js依赖的作用是 为了防止在IOS系统中拍照上传图片旋转90度问题。

后台搭建

1const Koa = require('koa');// koa框架 2const Router = require('koa-router');// 接口必备 3const cors = require('koa2-cors'); // 跨域必备 4const fs = require('fs'); // 文件系统 5const koaBody = require('koa-body'); //文件保存库 6const path = require('path'); // 路径 7 8let app = new Koa(); 9let router = new Router(); 10 11// 跨域 12app.use(cors({ 13    origin: function (ctx) { 14        return ctx.header.origin; 15    }, 16    exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], 17    maxAge: 5, 18    credentials: true, 19    withCredentials: true, 20    allowMethods: ['GET', 'POST', 'DELETE'], 21    allowHeaders: ['Content-Type', 'Authorization', 'Accept'], 22})); 23 24//上传文件限制 25app.use(koaBody({ 26    multipart: true, 27    formidable: { 28        maxFileSize: 1000 * 1024 * 1024 // 设置上传文件大小最大限制,默认10M 29    } 30})); 31 32// 上传图片 33router.post('/upload', async (ctx, next) => { 34    if (ctx.request.files.file) { 35        var file = ctx.request.files.file; 36        // 创建可读流 37        var reader = fs.createReadStream(file.path); 38        // 修改文件的名称 39        var myDate = new Date(); 40        var newFilename = myDate.getTime() + '.' + file.name.split('.')[1]; 41        var targetPath = path.join(__dirname, './images/') + `${newFilename}`; 42        //创建可写流 43        var upStream = fs.createWriteStream(targetPath); 44        // 可读流通过管道写入可写流 45        reader.pipe(upStream); 46        ctx.body = { 47            success: true, 48            name: newFilename, 49            msg:"压缩成功" 50        }; 51    } 52}); 53 54 55app.use(router.routes()).use(router.allowedMethods()); 56app.listen(6300) 57console.log('服务器运行中') 58 59

后台的逻辑其实很简单,就是实现一个接口,接收前台发来的文件,保存到本地目录上以及返回给前台状态。

结语

谢谢你的浏览,如果还有需要优化的地方请及时留言哦~


欢迎关注我的公众号「前端历劫之路

回复关键词「电子书」,即可获取近12本前端热门电子书。

回复关键词「红宝书第4版」,即可获取最新《JavaScript高级程序设计》(第四版)电子书。

你还可以加我微信,我拉拢了很多IT大佬,创建了一个技术交流、文章分享群,欢迎你的加入。

  • 作者:Vam的金豆之路

  • 主要领域:前端开发

  • 我的微信:maomin9761

  • 微信公众号:前端历劫之路


本文转转自微信公众号前端历劫之路原创https://mp.weixin.qq.com/s/HJzN5KVh9tommUYB4-71Uw,如有侵权,请联系删除。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )