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

Jacquelyn38
• 阅读 1633

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

前言

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

效果展示

原图片大小:82KB手把手教你实现一个图片压缩工具(Vue与Node的完美配合))压缩后的图片大小:17KB手把手教你实现一个图片压缩工具(Vue与Node的完美配合)

测试

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

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

教程

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

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

前台搭建

<template>  
  <div class="face">  
    <label for="file" class="inputlabelBox">  
      <input  
        type="file"  
        ref="pic"  
        id="file"  
        name="face"  
        accept="image/*"  
        capture="camera"  
        :style="{ display: 'none' }"  
        @change="handleClick"  
      />  
      <div class="upload">上传图片</div>  
    </label>  
    <div class="imgbox" v-show="imgsrc != ''">  
      <img src id="imgs" alt />  
    </div>  
    <div>  
      <p class="upload" @click="keepImg" v-show="imgsrc != ''">确定</p>  
    </div>  
  </div>  
</template>  
<script>  
import EXIF from "exif-js";  
export default {  
  name: "imgzip",  
  data() {  
    return {  
      imgsrc: "",  
    };  
  },  
  methods: {  
    // 上传图片  
    handleClick() {  
      if (this.$refs.pic.files[0]) {  

        // this.fileToBase64(this.$refs.pic.files[0]).then((res) => {  
        //   this.imgsrc = res;  
        // });  

        this.rotateImg(this.$refs.pic.files[0]).then((res) => {  
          this.imgsrc = res;  
        });  
      }  
    },  
    // 压缩和图片旋转  
    rotateImg(imgFile) {  
      return new Promise((resolve) => {  
        EXIF.getData(imgFile, function () {  
          let exifTags = EXIF.getAllTags(this);  
          let reader = new FileReader();  
          reader.readAsDataURL(imgFile);  
          reader.onload = (e) => {  
            let imgData = e.target.result;  
            document.querySelector("#imgs").src = e.target.result;  
            // 8 表示 顺时针转了90  
            // 3 表示 转了 180  
            // 6 表示 逆时针转了90  
            if (  
              exifTags.Orientation == 8 ||  
              exifTags.Orientation == 3 ||  
              exifTags.Orientation == 6  
            ) {  
              //翻转  
              //获取原始图片大小  
              const img = new Image();  
              img.src = imgData;  
              img.onload = function () {  
                let cvs = document.createElement("canvas");  
                let ctx = cvs.getContext("2d");  
                //如果旋转90  
                if (exifTags.Orientation == 8 || exifTags.Orientation == 6) {  
                  cvs.width = img.height;  
                  cvs.height = img.width;  
                } else {  
                  cvs.width = img.width;  
                  cvs.height = img.height;  
                }  
                if (exifTags.Orientation == 6) {  
                  //原图逆时针转了90, 所以要顺时针旋转90  
                  ctx.rotate((Math.PI / 180) * 90);  
                  ctx.drawImage(  
                    img,  
                    0,  
                    0,  
                    img.width,  
                    img.height,  
                    0,  
                    -img.height,  
                    img.width,  
                    img.height  
                  );  
                }  
                if (exifTags.Orientation == 3) {  
                  //原图逆时针转了180, 所以顺时针旋转180  
                  ctx.rotate((Math.PI / 180) * 180);  
                  ctx.drawImage(  
                    img,  
                    0,  
                    0,  
                    img.width,  
                    img.height,  
                    -img.width,  
                    -img.height,  
                    img.width,  
                    img.height  
                  );  
                }  
                if (exifTags.Orientation == 8) {  
                  //原图顺时针旋转了90, 所以要你时针旋转90  
                  ctx.rotate((Math.PI / 180) * -90);  
                  ctx.drawImage(  
                    img,  
                    0,  
                    0,  
                    img.width,  
                    img.height,  
                    -img.width,  
                    0,  
                    img.width,  
                    img.height  
                  );  
                }  
                let data = cvs.toDataURL("image/jpeg"); // 输出压缩后的base64  
                let arr = data.split(","),  
                  mime = arr[0].match(/:(.*?);/)[1], // 转成blob  
                  bstr = atob(arr[1]),  
                  n = bstr.length,  
                  u8arr = new Uint8Array(n);  
                while (n--) {  
                  u8arr[n] = bstr.charCodeAt(n);  
                }  
                let files = new window.File(  
                  [new Blob([u8arr], { type: mime })],  
                  "test.jpeg",  
                  { type: "image/jpeg" }  
                );  
                resolve(files);  
              };  
            } else {  
              let image = new Image(); //新建一个img标签(还没嵌入DOM节点)  
              image.src = e.target.result;  
              image.onload = function () {  
                let canvas = document.createElement("canvas"), // 新建canvas  
                  context = canvas.getContext("2d"),  
                  imageWidth = image.width, //压缩后图片的大小  
                  imageHeight = image.height,  
                  data = "";  
                canvas.width = imageWidth;  
                canvas.height = imageHeight;  
                context.drawImage(image, 0, 0, imageWidth, imageHeight);  
                data = canvas.toDataURL("image/jpeg"); // 输出压缩后的base64  
                let arr = data.split(","),  
                  mime = arr[0].match(/:(.*?);/)[1], // 转成blob  
                  bstr = atob(arr[1]),  
                  n = bstr.length,  
                  u8arr = new Uint8Array(n);  
                while (n--) {  
                  u8arr[n] = bstr.charCodeAt(n);  
                }  
                let files = new window.File(  
                  [new Blob([u8arr], { type: mime })],  
                  "test.jpeg",  
                  { type: "image/jpeg" }  
                ); // 转成file  
                resolve(files);  
              };  
            }  
          };  
        });  
      });  
    },  

    /*  
    fileToBase64(file) {  
      let that = this,  
        reader = new FileReader();  
      reader.readAsDataURL(file);  
      return new Promise((resolve, reject) => {  
        reader.onload = function (e) {  
          //这里是一个异步,所以获取数据不好获取在实际项目中,就用new Promise解决  
          if (this.result) {  
            let image = new Image(); //新建一个img标签(还没嵌入DOM节点)  
            image.src = e.target.result;  
            document.querySelector("#imgs").src = e.target.result;  
            image.onload = function () {  
              let canvas = document.createElement("canvas"), // 新建canvas  
                context = canvas.getContext("2d"),  
                imageWidth = image.width / 2, //压缩后图片的大小  
                imageHeight = image.height / 2,  
                data = "";  
              canvas.width = imageWidth;  
              canvas.height = imageHeight;  
              context.drawImage(image, 0, 0, imageWidth, imageHeight);  
              data = canvas.toDataURL("image/jpeg"); // 输出压缩后的base64  
              let arr = data.split(","),  
                mime = arr[0].match(/:(.*?);/)[1], // 转成blob  
                bstr = atob(arr[1]),  
                n = bstr.length,  
                u8arr = new Uint8Array(n);  
              while (n--) {  
                u8arr[n] = bstr.charCodeAt(n);  
              }  
              let files = new window.File(  
                [new Blob([u8arr], { type: mime })],  
                "test.jpeg",  
                { type: "image/jpeg" }  
              ); // 转成file  
              resolve(files);  
            };  
          } else {  
            reject("err");  
          }  
        };  
      });  
    },  
    */  


    // 保存图片  
    keepImg() {  
      // this.$emit("canvasToImage", this.imgsrc);  

      const fd = new FormData();  
      fd.append("file", this.imgsrc);  
      fetch("http://localhost:6300/upload", {  
        method: "post",  
        mode:"cors",  
        body:fd,  
      })  
        .then((response) => response.json())  
        .then((response) => {  
          if(response.success){  
            console.log(this.imgsrc);  
            const size = this.imgsrc.size<1024?this.imgsrc.size+"字节":Math.round(this.imgsrc.size/1024)+"KB";  
            console.log(size);  
            alert(`图片${response.name}${response.msg}!压缩后图片大小为:${size}。`);  
          }  
        })  
        .catch((err) => {  
          console.log(err);  
        });  
    },  
  },  
};  
</script>  
<style scoped lang="less">  
.upload {  
  display: inline-block;  
  background: #ffb90f;  
  color: white;  
  font-size: 16px;  
  text-align: center;  
  border-radius: 4px;  
  padding: 10px 30px;  
  margin-bottom: 20px;  
}  
.upload:hover {  
  filter: brightness(110%);  
}  
.upload:active {  
  filter: brightness(60%);  
}  
.imgbox {  
  text-align: center;  
  width: 60%;  
  margin: 0 auto;  
  img {  
    width: 100%;  
    height: 60vh;  
    object-fit: contain;  
  }  
}  
.face {  
  margin-top: 30px;  
  .container1 {  
    background: #000;  
    position: relative;  
    width: 580px;  
    height: 436px;  
    margin: 0 auto;  
    #canvas1 {  
      position: absolute;  
    }  
    video,  
    #canvas,  
    #canvas1 {  
      position: absolute;  
      top: 0;  
      left: 0;  
      right: 0;  
      bottom: 0;  
      width: 581px;  
      height: 436px;  
    }  
  }  
  .btns {  
    padding: 10px;  
    button {  
      margin: 20px 20px 20px 0;  
    }  
  }  
  .tips {  
    font-size: 26px;  
    color: #666;  
    margin: 10px 0;  
    line-height: 48px;  
  }  
  .imgs {  
    p {  
      font-size: 28px;  
    }  
  }  
}  
</style>  

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

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

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

后台搭建

const Koa = require('koa');// koa框架  
const Router = require('koa-router');// 接口必备  
const cors = require('koa2-cors'); // 跨域必备  
const fs = require('fs'); // 文件系统  
const koaBody = require('koa-body'); //文件保存库  
const path = require('path'); // 路径  

let app = new Koa();  
let router = new Router();  

// 跨域  
app.use(cors({  
    origin: function (ctx) {  
        return ctx.header.origin;  
    },  
    exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],  
    maxAge: 5,  
    credentials: true,  
    withCredentials: true,  
    allowMethods: ['GET', 'POST', 'DELETE'],  
    allowHeaders: ['Content-Type', 'Authorization', 'Accept'],  
}));  

//上传文件限制  
app.use(koaBody({  
    multipart: true,  
    formidable: {  
        maxFileSize: 1000 * 1024 * 1024 // 设置上传文件大小最大限制,默认10M  
    }  
}));  

// 上传图片  
router.post('/upload', async (ctx, next) => {  
    if (ctx.request.files.file) {  
        var file = ctx.request.files.file;  
        // 创建可读流  
        var reader = fs.createReadStream(file.path);  
        // 修改文件的名称  
        var myDate = new Date();  
        var newFilename = myDate.getTime() + '.' + file.name.split('.')[1];  
        var targetPath = path.join(__dirname, './images/') + `${newFilename}`;  
        //创建可写流  
        var upStream = fs.createWriteStream(targetPath);  
        // 可读流通过管道写入可写流  
        reader.pipe(upStream);  
        ctx.body = {  
            success: true,  
            name: newFilename,  
            msg:"压缩成功"  
        };  
    }  
});  


app.use(router.routes()).use(router.allowedMethods());  
app.listen(6300)  
console.log('服务器运行中')  

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

结语

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


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

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

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

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

  • 作者:Vam的金豆之路

  • 主要领域:前端开发

  • 我的微信:maomin9761

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

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


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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Linux递归压缩图片脚本
1压缩图片使用ImageMagick的convert命令进行压缩图片,一般只需要一个指定压缩质量的参数,比如:convertquality751.jpg1_compress.jpg可以支持压缩jpg/png/jpeg。2递归压缩递归压缩使用find配合g
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
3年前
Guetzli:谷歌家的东西可能也没有想像的辣么美
望昕宇,腾讯后台工程师,专注图片压缩及存储系统一百年不动摇,并致力于做一名相关前沿技术的人话翻译家。这两天笔者的朋友圈被Google开源JPEG编码器Guetzli刷屏,“图片大小减小35%”、“质量不变”这样的字眼刺激了我们的肾上腺,OMG的yajunwang同学也为我们带来了第一手的测试资料——谷歌开源图片压缩算法Guetzli实测体验报告(
Stella981 Stella981
3年前
HTML5 file API加canvas实现图片前端JS压缩并上传
一、图片上传前端压缩的现实意义对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提高了用户体验。这种体验包括两方面:1.由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅,同时大大降低了网络异常导致上传失败风险。2.最最重要的体验改进点:省略了图片的再加工成本。很多网站的图片上传功能都会对图片的大小进
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这