一、前情提要
上一次我们完成了历史工单管理接口的开发,由于实训楼和实训室管理的开发也是比较简单的服务调用而已,所以在这里就不再赘述了,这一次我们来做二维码的相关开发,实现二维码生成,Zip压缩,制作定时任务。
二、接口功能设想
这个项目需要二维码的原因是这样的,我当初的设想是在每台电脑前都有一个相应的二维码,只需要扫一扫二维码,然后会出现对应的电脑位置信息,如在哪栋实训楼,实训室,电脑编号是什么,从而不需要用户来手动输入电脑位置信息。用户只需要编写电脑出现的问题,必要时上传图片,然后写上自己的信息提交即可。而管理员也可以按照报修信息准确找到相应的电脑。
三、接口设计及实现
1.导入相应的第三方工具
在pom.xml
中编写以下代码,导入第三方工具
- 二维码生成插件,二维码插件的GitHub地址
这个插件不仅仅可以生成二维码,它还能提供图片 + 音频 + 视频处理,如果需要其他功能请查看该项目的ReadMe。 - 压缩文件,zip4j官网
压缩文件用于应对生成图像时可能会不止一张,如果让管理员一张张地下载会很麻烦,所以需要将生成的图片进行压缩打包后提供给管理员下载。
<!--生成二维码的外部项目源-->
<!--请务必先写好外部项目源,然后再导入相应插件,不然会找不到包报错-->
<repositories>
<repository>
<id>yihui-maven-repo</id>
<url>https://raw.githubusercontent.com/liuyueyi/maven-repository/master/repository</url>
</repository>
</repositories>
<!--二维码生成器插件-->
<dependency>
<groupId>com.github.hui.media</groupId>
<artifactId>qrcode-plugin</artifactId>
<version>1.0</version>
</dependency>
<!--压缩文件上传工具-->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>1.3.2</version>
</dependency>
2.定义好二维码生成地址的常量
在com.repairsystem.utils.ConstantUtils
定义好生成二维码的位置
class Path {
//虚拟目录地址
public static final String DIRPATH = "/opt/Image";
public static final String QRCODEPATH = "/QRCODE";
}
3.创建二维码生成工具QRCodeUtils
由于二维码生成器插件只会生成一个二维码,并不会在二维码隔壁再写一些内容,如实训楼、实训室、电脑编号。所以我们需要对于生成的二维码进行再次加工。
实现步骤:
1.判断生成的图片是一张还是多张。
2.使用插件生成二维码,二维码内容为报修表单地址,带有实训楼ID和名称、实训室ID和名称、电脑编号参数。
3.使用BufferedImage
创建一张700*300的画布,填充为白色。
4.把二维码的图像流放入画布中。
5.把需要的信息定位到画布中。
6.把图像流导出到一个文件夹中,如果没有该文件夹则生成文件夹,如果是多张二维码生成则不断循环步骤2~6。
7.最后返回生成二维码文件夹位置。
在com.repairsystem.utils
创建二维码生成工具QRCodeUtils
package com.repairsystem.utils;
import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeGenWrapper;
import com.google.zxing.WriterException;
import com.repairsystem.entity.Class;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* @author CheungChingYin
* @date 2018/11/18
* @time 21:37
*/
public class QRCodeUtils {
/**
* @param domain
* @param clazz
* @param computerStartNum
* @param computerEndNum
* @return存储图片的文件夹地址
*/
public static String generateQRCode(String domain, Class clazz, Integer computerStartNum, Integer computerEndNum) {
if (computerStartNum.equals(computerEndNum)) {
BufferedImage bufferedImage = null;
try {
bufferedImage = QrCodeGenWrapper.of("http://" + domain + "?buildingId=" + clazz.getBuildingId() + "&buildingName=" + clazz.getBuildingName() + "&classId=" + clazz.getClassId() + "&className=" + clazz.getClassName() + "&computerNum=" + computerStartNum)
.setW(300)
.setH(300)
.asBufferedImage();
} catch (IOException e) {
e.printStackTrace();
} catch (WriterException e) {
e.printStackTrace();
}
BufferedImage picture = new BufferedImage(700, 300, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) picture.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, 700, 300);
g.drawImage(bufferedImage, 0, 0, null);
g.setColor(Color.BLACK);
g.setFont(new Font("宋体", Font.PLAIN, 20));
g.drawString("所在实训楼:" + clazz.getBuildingName(), 350, 50);
g.drawString("所在实训室:" + clazz.getClassName(), 350, 100);
g.drawString("电脑编号:" + computerStartNum, 350, 150);
g.drawString("若当前电脑出现问题", 350, 200);
g.drawString("请扫描左侧二维码进行报修", 350, 250);
g.dispose();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String currentDate = simpleDateFormat.format(new Date());
String realPath = ConstantUtils.Path.DIRPATH + ConstantUtils.Path.QRCODEPATH + "/" + currentDate + "/" + UUID.randomUUID() + "/";
String fileName = clazz.getBuildingName() + "-" + clazz.getClassName() + "-" + computerStartNum + ".jpg";
File outPutFileDir = new File(realPath);
if (!outPutFileDir.exists()) {
outPutFileDir.mkdirs();
}
File outPutFile = new File(realPath + fileName);
try {
ImageIO.write(picture, "jpg", outPutFile);
} catch (IOException e) {
e.printStackTrace();
}
return realPath;
} else {
BufferedImage bufferedImage = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String currentDate = simpleDateFormat.format(new Date());
String realPath = ConstantUtils.Path.DIRPATH + ConstantUtils.Path.QRCODEPATH + "/" + currentDate + "/" + UUID.randomUUID() + "/";
File outPutFileDir = new File(realPath);
if (!outPutFileDir.exists()) {
outPutFileDir.mkdirs();
}
for (int i = computerStartNum; i <= computerEndNum; i++) {
try {
bufferedImage = QrCodeGenWrapper.of("http://" + domain + "?buildingId=" + clazz.getBuildingId() + "&buildingName=" + clazz.getBuildingName() + "&classId=" + clazz.getClassId() + "&className=" + clazz.getClassName() + "&computerNum=" + i)
.setW(300)
.setH(300)
.asBufferedImage();
BufferedImage picture = new BufferedImage(700, 300, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) picture.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, 700, 300);
g.drawImage(bufferedImage, 0, 0, null);
g.setColor(Color.BLACK);
g.setFont(new Font("微软雅黑", Font.PLAIN, 20));
g.drawString("所在实训楼:" + clazz.getBuildingName(), 350, 50);
g.drawString("所在实训室:" + clazz.getClassName(), 350, 100);
g.drawString("电脑编号:" + i, 350, 150);
g.drawString("若当前电脑出现问题", 350, 200);
g.drawString("请扫描左侧二维码进行报修", 350, 250);
g.dispose();
String fileName = clazz.getBuildingName() + "-" + clazz.getClassName() + "-" + i + ".jpg";
File outPutFile = new File(realPath + fileName);
ImageIO.write(picture, "jpg", outPutFile);
} catch (IOException e) {
e.printStackTrace();
} catch (WriterException e) {
e.printStackTrace();
}
}
return realPath;
}
}
}
4.创建压缩工具类ZipUtils
在com.repairsystem.utils
创建压缩工具类ZipUtils
package com.repairsystem.utils;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
/**
* @author CheungChingYin
* @date 2018/11/19
* @time 14:23
*/
public class ZipUtils {
//声明压缩对象
private static ZipParameters parameters;
//解压文件对象
private static ZipFile zipFile;
/**
*
* @param sourceFilePath 被压缩的文件的路径(单文件,文件夹)
* @param zipFilePath 压缩文件路径
* @param password 压缩密码
* @return 压缩成功:true ,压缩失败:false
*/
public static Boolean singleFileCompress(String sourceFilePath,String zipFilePath,String password){
parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); // 压缩方式(默认方式)
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); // 压缩级别(默认级别)
//压缩加密设置
if (!StringUtils.isEmpty(password)) {
parameters.setEncryptFiles(true);//是否设置文件加密(默认为否)
parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD); // 加密方式(此处是标准压缩)
parameters.setPassword(password.toCharArray());
}
try {
ZipFile zipFile = new ZipFile(zipFilePath);
//如果是文件则直接压缩,若是文件夹,遍历文件全部压缩
if(new File(sourceFilePath).isFile()) {
zipFile.setFileNameCharset("GBK");
zipFile.addFile(new File(sourceFilePath), parameters);
return true;
}
//File ff=new File(sourceFilePath);
File[] flst=new File(sourceFilePath).listFiles();
System.out.println("文件个数=>"+flst.length);
for(File f:flst){
zipFile.setFileNameCharset("GBK");
zipFile.addFile(f, parameters);
}
return true;
} catch (ZipException e) {
e.printStackTrace();
return false;
}catch (Exception id){
id.printStackTrace();
return false;
}
}
public static Boolean unZip(String zipFile,String unZipDir){
try {
ZipUtils.zipFile = new ZipFile(zipFile);
ZipUtils.zipFile.setFileNameCharset("GBK");//设置编码格式
//用自带的方法检测一下zip文件是否合法,包括文件是否存在、是否为zip文件、是否被损坏等
if (!ZipUtils.zipFile.isValidZipFile()) {
throw new ZipException("文件不合法或不存在");
}
// 跟java自带相比,这里文件路径会自动生成,不用判断
ZipUtils.zipFile.extractAll(unZipDir);
return true;
}catch(ZipException e){
return false;
}
}
}
5.创建接口二维码相关接口QRCodeController
在com.repairsystem.web.controller
创建接口二维码相关接口QRCodeController
提供下载功能需要在响应头上设置application/force-download
package com.repairsystem.web.controller;
import com.repairsystem.entity.Class;
import com.repairsystem.utils.QRCodeUtils;
import com.repairsystem.utils.ZipUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
/**
* @author CheungChingYin
* @date 2018/11/19
* @time 20:10
*/
@Controller
@Api(value = "二维码相关接口", tags = {"二维码相关接口"})
@RequestMapping("/QRCode")
public class QRCodeController {
@ApiOperation(value = "下载二维码图片")
@ApiImplicitParams({
@ApiImplicitParam(name = "domain", value = "当前域名", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "buildingId", value = "所属实训楼Id", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "buildingName", value = "所属实训楼名称", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "classId", value = "所属实训室ID", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "className", value = "所属实训室名称", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "computerStartNum", value = "电脑开始编号", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "computerEndNum", value = "电脑结束编号", required = true, dataType = "String", paramType = "query"),
})
@GetMapping("/QRCodeDownLoad")
public String downloadQRCode(String domain, Integer buildingId, String buildingName, Integer classId, String className,
Integer computerStartNum, Integer computerEndNum, HttpServletResponse response) {
Class clazz = new Class();
clazz.setBuildingId(buildingId);
clazz.setBuildingName(buildingName);
clazz.setClassId(classId);
clazz.setClassName(className);
String dirPath = QRCodeUtils.generateQRCode(domain, clazz, computerStartNum, computerEndNum);
String zipFileName = buildingName + "&" + className + ".zip";
String zipFilePath = dirPath + zipFileName;
boolean zipResult = ZipUtils.singleFileCompress(dirPath, zipFilePath, null);
if (zipResult) {
response.setContentType("application/force-download");// 设置强制下载不打开
response.addHeader("Content-Disposition", "attachment;fileName=QRCode.zip");// 设置文件名
File file = new File(zipFilePath);
if (file.exists()) {
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(file);
IOUtils.copy(fis, response.getOutputStream());
response.flushBuffer();
System.out.println("success");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
6.定时任务:删除生成的二维码图片
在我们生成了二维码后,二维码会存在我们的服务器中,如果不定时删除图片会造成服务器的空间不足,所以在这里我们利用SpringBoot自带的定时器来做一个定时删除图片功能。
定时功能只需要使用@Scheduled
注解即可,更多关于@Scheduled
的用法请查看此网站:点我跳转
首先在RepairsystemApplication
中开启定时器,只需要在类的头上添加@EnableScheduling
注解即可
然后在com.repairsystem.config
创建定时任务类SchedulerTask
package com.repairsystem.config;
import com.repairsystem.utils.ConstantUtils;
import org.apache.commons.io.FileUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
/**
* @author CheungChingYin
* @date 2018/11/20
* @time 10:55
*/
@Component
public class SchedulerTask {
/**
* 每天凌晨3点删除生成的QRCode文件
*/
@Scheduled(cron = "0 0 3 * * ?")
private void deleteQRCodeFile(){
File file = new File(ConstantUtils.Path.DIRPATH+ ConstantUtils.Path.QRCODEPATH);
if (file.exists()){
try {
FileUtils.cleanDirectory(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
到这里,Controller层开发——二维码相关接口以及定时任务已经开发完成了,也就意味着【机房报修管理系统】 后端篇已经结束,接下来我将会更新前端篇,如果各位同学对前端有兴趣可以来看一下。如果您对次篇文章有疑问,可以在文章下方留言,谢谢您的阅读。如对【机房报修管理系统】系列文章有兴趣,可以关注或收藏我的文章,您的支持是我最大的动力,我会尽快推出下一期内容,敬请期待。