目录

场景

方案分析

第一步——引入依赖

第二步——编写工具类

第三步——编写API接口

第四步——访问测试

第五步——下载图片


场景

最近笔者做的项目中,有一个需求:

在系统中生成一个二维码,用户保存下来并分享出去,其他人扫描之后跳到我们的一个活动详情页,查看此活动的内容。

从以上的需求中,可以提炼出以下几点:

  1. 当用户点击生成二维码的时候,我们要拿到用户生成的二维码是关于哪个活动的;
  2. 请求来到后台,拿到活动ID,作为我们活动详情页的参数,生成一个url,作为扫描之后的跳转地址,生成二维码;
  3. 把生成的二维码,返回给前端,展示给用户;
  4. 用户保存二维码,即下载下来。

以上就是全部业务流程,其中第一步、第二步无关紧要,只是给二维码设置内容而已。这里需要提一句,二维码的内容,可以是一段明文,也可以是一个http或https链接,当扫描时会自动访问这个链接。对于大部分的场景,不能说大部分,应该说99%以上的场景都是给二维码的内容设置为一个链接,扫描之后跳到对应的页面再去执行业务,除非你玩个花样,设置内容为明文“我爱你”,然后把二维码发给你的女朋友……

方案分析

我们重点分析第三步和第四步,即二维码的生成和用户下载二维码。

因为第一步、第二步就是怎样生成二维码,市面上有很多依赖包,拿来用就行,重点是如何优雅地返回给用户以及供其下载。其实让用户下载这个动作,也不是很必要,毕竟大多数年轻人都知道长按保存,但还是要照顾一下其他的用户,给出明显的下载按钮。

先说第三步,二维码如何返回给前端?也许很多人会想到,把生成的二维码存到服务器上,再把图片路径返回不就行了?这样当然可以,但是却多了很多不必要的操作:

  1. 对于二维码,我们只需要返回给用户即可,并不需要存到我们的服务器上,这没有任何意义,还占用磁盘空间;
  2. 如果将二维码写到服务器,就需要跟磁盘IO打交道,这是昂贵的代价;
  3. 每一个用户生成的二维码,都对应一个URL,很乱

我们可以直接将生成的二维码图片,以IO流的方式,通过response响应体直接返回给请求方。第一,不需要落到我们的磁盘,一切操作都在内存中完成,效率比较高;第二,所有生成二维码的请求,都可以访问这里,前端直接拿img标签的src就能访问,你在浏览器直接输入这个路径也能得到一张图片,减少了很多交互和逻辑处理。

再说第四步,我们直接在前端实现,因为前端有一种机制可以将某个img标签的图片下载下来,不需要浪费我们的服务器。

第一步——引入依赖

需要引入两个依赖包:

<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.0.0</version></dependency><dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.0.0</version></dependency>

第二步——编写工具类

这里需要声明一下,以下工具类中去掉边框的代码,是博主从网上的一篇文章找的,但是博主已经找不到是哪篇文章了,而且好像很多文章都有这段代码,他们还都是原创……

package com.dosion.utils;import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;/***  <p>*      生成二维码的工具类*  </p>** @author 秋枫艳梦* @date 2019-08-22* */
public class QRCodeUtils {/***  生成二维码* @param content 二维码的内容* @return BitMatrix对象* */public static BitMatrix createCode(String content) throws IOException {//二维码的宽高int width = 200;int height = 200;//其他参数,如字符集编码Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");//容错级别为Hhints.put(EncodeHintType.ERROR_CORRECTION , ErrorCorrectionLevel.H);//白边的宽度,可取0~4hints.put(EncodeHintType.MARGIN , 0);BitMatrix bitMatrix = null;try {//生成矩阵,因为我的业务场景传来的是编码之后的URL,所以先解码bitMatrix = new MultiFormatWriter().encode(content,BarcodeFormat.QR_CODE, width, height, hints);//bitMatrix = deleteWhite(bitMatrix);} catch (WriterException e) {e.printStackTrace();}return bitMatrix;}/***  删除生成的二维码周围的白边,根据审美决定是否删除* @param matrix BitMatrix对象* @return BitMatrix对象* */private static BitMatrix deleteWhite(BitMatrix matrix) {int[] rec = matrix.getEnclosingRectangle();int resWidth = rec[2] + 1;int resHeight = rec[3] + 1;BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);resMatrix.clear();for (int i = 0; i < resWidth; i++) {for (int j = 0; j < resHeight; j++) {if (matrix.get(i + rec[0], j + rec[1]))resMatrix.set(i, j);}}return resMatrix;}
}

第三步——编写API接口

/***  生成二维码* @param type 二维码的类型,为了演示效果,1跳百度的,2是跳京东* */@GetMapping(value = "/activity/create/activity-code")@ApiOperation("生成活动详情二维码")public void getCode(int type , HttpServletResponse response) throws IOException {// 设置响应流信息response.setContentType("image/jpg");response.setHeader("Pragma", "no-cache");response.setHeader("Cache-Control", "no-cache");response.setDateHeader("Expires", 0);OutputStream stream = response.getOutputStream();//type是1,生成活动详情、报名的二维码,type是2,生成活动签到的二维码String content = (type == 1 ? "http://www.baidu.com" : "http://www.jd.com");//获取一个二维码图片BitMatrix bitMatrix = QRCodeUtils.createCode(content);//以流的形式输出到前端MatrixToImageWriter.writeToStream(bitMatrix , "jpg" , stream);}

第四步——访问测试

扫描以上二维码,就会跳转到百度,如果type给2就会跳转到京东。

这样一来,我们的二维码图片根本就不用落到服务器的磁盘,以流的方式就可以让前端直接访问。而且这个生成二维码的接口完全可以共用,因为它不保存二维码,只是返回图片的二进制流,你传入对应的参数我就生成不同的二维码,再返回对应的流。甚至二维码的目标链接你也可以让前端作为参数传给你(但是前端要进行URL Encode),减少了复杂性。

在项目中的实际效果:

用户点击生成二维码,将img标签的src属性指向我们的API接口即可,然后这个图片就可以显示出来了。

第五步——下载图片

其实这一步,完全就是前端的工作,你就不用管了,但还是记录一下这段脚本,万一什么时候用到了呢。

function downloadImage(imgsrc, name) {//下载图片地址和图片名let image = new Image();// 解决跨域 Canvas 污染问题image.setAttribute("crossOrigin", "anonymous");image.onload = function() {let canvas = document.createElement("canvas");canvas.width = image.width;canvas.height = image.height;let context = canvas.getContext("2d");context.drawImage(image, 0, 0, image.width, image.height);let url = canvas.toDataURL("image/jpg"); //得到图片的base64编码数据let a = document.createElement("a"); // 生成一个a元素let event = new MouseEvent("click"); // 创建一个单击事件a.download = name || "photo"; // 设置图片名称a.href = url; // 将生成的URL设置为a.href属性a.dispatchEvent(event); // 触发a的单击事件};image.src = imgsrc;}

用法也很简单,imgSrc就是图片的地址,在这里指向我们上面的API就行,因为我们的API响应的就是一张图片嘛。name就是下载之后的文件名。

如有任何问题,欢迎与博主联系

Java生成二维码并以IO流的形式返回给前端展示(不需写入服务器),以及下载二维码图片相关推荐

  1. JAVA读取文本文件内容以流的形式返回到前端

    JAVA读取文本文件内容以流的形式返回到前端 此方法放Controller ServletOutputStream out = null;ByteArrayOutputStream baos = nu ...

  2. 【程序员养成之路】Java基础篇 8-流进流出的IO流(二)

    以下内容若有误,欢迎私信我或在下方留言,谢谢^_− 目录 IO流(二) 1.特殊操作流 1.1 标准流 1.2 打印流 1.3 对象序列化流 1.4 Properties 拓展1:比较字节流和字节缓冲 ...

  3. 黑马毕向东Java课程笔记(day20-1——20-17)IO流:File类及相关方法、递归、递归的相关练习、Properties、PrintWriter类与PrintStream类、合并流与切割流

    1.File类概述   File是文件和目录路径名的抽象表示形式. 用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作.   前面说到的"流",它只能操作数据,想 ...

  4. Educoder–Java高级特性(第一章)- IO流【笔记+参考代码】

    Educoder–Java高级特性(第一章)- IO流[笔记+参考代码] 第一关 1.下列关于字节和字符的说法正确的是(BC) A.字节 = 字符 + 编码 B.字符 = 字节 + 编码 C.字节 = ...

  5. JavaScript实现以数组形式返回斐波那契数列fibonacci算法(附完整源码)

    JavaScript实现以数组形式返回斐波那契数列fibonacci算法(附完整源码) fibonacci.js完整源代码 fibonacci.js完整源代码 export default funct ...

  6. Java基础知识(三十一)IO流(二) File类、递归、IO流基础

    目录 一. File类 1.File: 2.构造方法: 3.创建功能 4.删除功能 5.  重命名功能 6.判断功能 7.基本获取功能 8.高级获取功能 9.需求: 二.递归 1.概述: 2.   递 ...

  7. Java中使用try-with-resource优雅的关闭io流

    前言 最近看到一篇关于串流的关闭方法,通过编译器自动生成关闭串流源码,行之有效. 转载自博主: Java劝退师. https://blog.csdn.net/qq_41389354/article/d ...

  8. java 发邮件带excel附件,以流的形式发送附件,不生成excel文件

    公司需求:根据业务需要定时发送邮件,并且邮件需要有excel附件,附件内容为查询到的数据. 开发思路:由于服务器环境本身空间有限,而且不确定环境中读写权限,生成excel文件比较浪费资源,出现异常时可 ...

  9. 第三次学JAVA再学不好就吃翔(part101)--IO流

    学习笔记,仅供参考 文章目录 IO流 IO流的概述及其分类 FileInputStream 构造方法 成员方法 举个例子 FileOutputStream 构造方法 成员方法 举个例子 IO流 IO流 ...

最新文章

  1. [CVPR 2016] Weakly Supervised Deep Detection Networks论文笔记
  2. 教AI逐帧搓招玩《铁拳》通关最高难度,现在的街机游戏爱好者有点东西啊
  3. zemax图像模拟_zemax2014免费版
  4. 用python绘制漂亮的图形-用Python画一些漂亮图形--Quora代码赏析
  5. JavaScript 技术篇-js字符串大小写转换,toLocalUpperCase()和toUpperCase()的区别详解
  6. python 奇偶链表
  7. 零配置 之 Spring注解实现Bean定义
  8. javascript-04 引用类型
  9. Hibernate事务
  10. php列表代码怎么做的,php项目实战:代码讲解之订单列表问题
  11. python教程推荐-入门python有什么好的书籍推荐?
  12. allegro中10mil过孔_Allegro中增加过孔的方法
  13. linux下用C/C++和socket实现的驾校约车助手
  14. python dictionary怎么用_python Dictionary字典使用
  15. Domain Adaptation for Object Detection using SE Adaptors and Center Loss 论文翻译
  16. shell基础之04
  17. oracle补充日志
  18. php time 转化,php时间戳转换
  19. 李岳恒:下沉市场——最后的增长点
  20. 前后端分离 -- 深入浅出 Spring Boot + Vue + ElementUI 实现相册管理系统【文件上传 分页 】 文件上传也不过如此~

热门文章

  1. js获取浏览器当前屏幕的宽度和高度
  2. android 使用mupdf,android-在应用程序中集成MuPDF Reader
  3. QString QChar int 互转
  4. NIUSHOP wap端首页导航菜单
  5. UE4(虚幻4)基础:创建玩家角色并获得控制权
  6. INPHO常见问题处理之新建新椭球定义
  7. 在命令行中打开某个文件夹
  8. 基于matlab的智能车仿真程序
  9. python检测键盘按键命令_python实时检测键盘输入函数的示例
  10. 定位技术的关键:基于环境特征的地图匹配(三)