前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >手把手教你实现一个图片压缩工具(Vue与Node的完美配合)

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

作者头像
Vam的金豆之路
发布于 2021-12-01 01:17:52
发布于 2021-12-01 01:17:52
80800
代码可运行
举报
文章被收录于专栏:前端历劫之路前端历劫之路
运行总次数:0
代码可运行

前言

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

效果展示

原图片大小:82KB

压缩后的图片大小:17KB

测试

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

教程

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

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

前台搭建

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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度问题。

后台搭建

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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('服务器运行中')

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

结语

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端历劫之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
写一只具有识别能力的图片爬虫
在网上看到python做图像识别的相关文章后,真心感觉python的功能实在太强大,因此将这些文章总结一下,建立一下自己的知识体系。 当然了,图像识别这个话题作为计算机科学的一个分支,不可能就在本文简单几句就说清,所以本文只作基本算法的科普向。如有错误,请多包涵和多多指教。 本文参考文章和图片来源 wbj0110的文章 http://soledede.iteye.com/blog/1940910 赖勇浩的文章 http://blog.csdn.net/gzlaiyonghao/article/detai
机器学习AI算法工程
2018/03/13
2K0
写一只具有识别能力的图片爬虫
Python学习案例之图片人脸检测识别
随着科技的发展,人脸识别技术在许多领域得到的非常广泛的应用,手机支付、银行身份验证、手机人脸解锁等等。
小柒2012
2019/12/05
8860
干货 | 手把手教你运用Python实现简单的人脸识别
Python里,简单的人脸识别有很多种方法可以实现,依赖于python胶水语言的特性,我们通过调用包可以快速准确的达成这一目的。这里介绍的是准确性比较高的一种。
用户1621951
2019/06/06
1.4K0
干货 | 手把手教你运用Python实现简单的人脸识别
Python-OpenCV人脸检测(代码)
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012162613/article/details/43523507
李智
2019/05/26
1.9K0
基于OpenCv的人脸识别(Python完整代码)
采集人脸图片的方法多种多样,可以直接从网上下载数据集,可以从视频中提取图片,还可以从摄像头实时的采集图片。
全栈程序员站长
2022/06/26
6K0
基于OpenCv的人脸识别(Python完整代码)
10行代码实现python人脸识别
人脸识别,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别的一系列相关技术,通常也叫做人像识别、面部识别。
星星在线
2020/05/22
5.1K1
手把手教你如何用 OpenCV + Python 实现人脸识别
下午的时候,配好了 OpenCV 的 Python 环境,OpenCV 的 Python 环境搭建。于是迫不及待的想体验一下 opencv 的人脸识别,如下文。 必备知识 Haar-like Haar-like 百科释义。通俗的来讲,就是作为人脸特征即可。 Haar 特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。 opencv api 要想使用 opencv,就必须先知道其能干什么,怎么做。于是 AP
AI研习社
2018/03/28
2.4K0
手把手教你如何用 OpenCV + Python 实现人脸识别
python-opencv人脸识别与树莓派摄像头转头跟随()
代码发布在github中https://github.com/luyishisi/The_python_code.git文件夹是face-gensui
十四君
2019/11/28
1.6K0
图像处理智能化的探索[一]:人脸识别裁图
最近在对接公司一些新闻接口的时候,发现接口茫茫多:CMS接口、无线CMS接口、正文接口、列表接口……更令人捉急的是,由于新闻推送场景不同,每条新闻的配图尺寸也就不同,比如PC要求高清大图,而移动端就会根据屏幕尺寸要求各种尺寸的小图,一个接口也就要吐出好几个尺寸的图片供客户端使用。比如无线CMS的接口里就需要640330、150120、280*210……那么问题来了,难道每多一种尺寸就需要编辑裁一次图上传到CMS?
星回
2018/08/02
1.6K0
图像处理智能化的探索[一]:人脸识别裁图
基于OpenCV实现简单人脸面具、眼镜、胡须、鼻子特效(详细步骤 + 源码)
本文给大家分享一个基于OpenCV实现简单人脸面具、眼镜、胡须、鼻子特效的实例,并附实现步骤和源码。
Color Space
2022/04/06
2.5K0
基于OpenCV实现简单人脸面具、眼镜、胡须、鼻子特效(详细步骤 + 源码)
关于OpenCV for Python入门-自带人脸检测算法比较
本来学习OpenCV的目的就是为了做人脸识别、车辆识别、人群计数等等,识别人脸首先要进行人脸检测,OpenCV中内置了Haar Cascade人脸分类器,其中包括haarcascade_frontalface_alt、haarcascade_frontalface_alt_tree、haarcascade_frontalface_alt2、haarcascade_frontalface_default这四种,本文不求甚解,只是从比对上判断一下这几种内置分类器的可用性。
python与大数据分析
2022/04/02
6810
关于OpenCV for Python入门-自带人脸检测算法比较
数据魔术师小白零基础实现简单人脸识别
本文为零基础实现人脸识别的教程,读者不需要人工智能学习背景,不需要机器学习相关基础,只要能读懂简单的Pyhton代码,便可以轻松地在自己的电脑上实现人脸识别(两个文件,加注释共96行)。
用户1621951
2020/10/22
1.1K0
数据魔术师小白零基础实现简单人脸识别
C# 使用OpenCV在一张图片里寻找人脸
例程中用到一个库叫做emgucv,是opencv\的net封装 编译打包好的稳定版,在这:https://sourceforge.net/projects/emgucv/files/emgucv/ 如果要最新代码,在这里获取:https://github.com/emgucv/emgucv
zls365
2020/08/19
2.8K0
C# 使用OpenCV在一张图片里寻找人脸
简单人脸识别一之使用opencv+cnn网络实现人脸识别
最近在研究目标检测这个方向,看到网上有很多的人脸识别帖子,所以也想着上上手看看。当时是做了三个模型出来,第一个就是网上很通用普遍的opencv+简单三层cnn网络来实现的,说实话效果真的一般吧!具体的下面再细细陈述。第二个是把三层cnn网络换成了残差网络。因为自己刚好也是学习了残差网络。就想着生搬硬套过来,但效果说实话很迷,时好时坏,把我是整蒙逼了,后面也会提的。最后一个是用opencv+MTCNN+FaceNet来实现的,效果就比较好了,训练速度快,检测人脸的准确率也比前两个模型更好。我接下来会写三篇文章来一一介绍!
全栈程序员站长
2022/08/26
1.9K0
简单人脸识别一之使用opencv+cnn网络实现人脸识别
OpenCV人脸识别之三:识别自己的脸
本系列人脸识别文章用的是opencv2,最新版的opencv3.2的代码请参考文章: OpenCV之识别自己的脸——C++源码放送(请在上一篇文章末尾查看) 在之前《OpenCV人脸识别之一:数据收集和预处理》和《OpenCV人脸识别之二:模型训练》两篇博客中,已经把人脸识别的整个流程全部交代清楚了。包括今天这篇人脸识别方面的内容都已经在上述第二篇博客中的代码中有所体现。只是今天的内容会让结果更加的形象化。仅此而已。可以说,本篇的内容是前面诸多内容的一个整合。所以今天的内容也很简洁。 简单说下流程 1、打
用户1332428
2018/03/09
1.5K0
OpenCV人脸识别之三:识别自己的脸
人工智能视觉:基于OpenCV的人脸识别技术的深度解析
OpenCV 的全称是 Open Source Computer Vision Library,是一个跨平台的计算机视觉库。OpenCV 是由英特尔公司发起并参与开发,以 BSD 许可证授权发行,可以在商业和研究领域中免费使用。OpenCV 可用于开发实时的图像处理、计算机视觉以及模式识别程序。该程序库也可以使用英特尔公司的 IPP 进行加速处理。
爱喝兽奶的熊孩子
2024/05/24
3K0
人工智能视觉:基于OpenCV的人脸识别技术的深度解析
openCV人脸识别简单案例[通俗易懂]
我们使用机器学习的方法完成人脸检测,首先需要大量的正样本图像(面部图像)和负样本图像(不含面部的图像)来训练分类器。我们需要从其中提取特征。下图中的 Haar 特征会被使用,就像我们的卷积核,每一个特征是一 个值,这个值等于黑色矩形中的像素值之后减去白色矩形中的像素值之和。
全栈程序员站长
2022/09/01
8020
就是这么霸道,使用OpenCV10行代码实现人脸检测
虽然互联网上有很多关于 OpenCV 的 Haar Cascade 对象检测模块这方面的技术资料,但这篇文章的重点是通俗易懂地解释这些概念,希望这能帮助初学者以简单的方式理解 Python 的 OpenCV 库。
小白学视觉
2022/02/14
1.1K0
就是这么霸道,使用OpenCV10行代码实现人脸检测
手把手教你opencv做人脸识别(附源码+文档)
python3.9 pycharm2020 人狠话不多,直接上代码,注释在代码里面,不说废话。
全栈程序员站长
2022/06/27
1K0
手把手教你opencv做人脸识别(附源码+文档)
利用python、tensorflow、opencv实现人脸识别(包会)!
本人是机械专业在读硕士,在完成暑假实践的时候接触到了人脸识别,对这一实现很感兴趣,所以花了大概十天时间做出了自己的人脸识别。这篇文章应该是很详细的了所以帮你实现人脸识别应该没什么问题。
全栈程序员站长
2022/11/17
4K0
利用python、tensorflow、opencv实现人脸识别(包会)!
推荐阅读
相关推荐
写一只具有识别能力的图片爬虫
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验