生成二维码
第一步:导依赖包(使用的是Qrcode的方法)
<!--生成二维码,这个jar包可能不容易下载,需要手动将jar包放入maven仓库里-->
<dependency>
<groupId>Qrcode_swetake</groupId>
<artifactId>Qrcode_swetake</artifactId>
<version>1.0</version>
</dependency>
<!--base64流,应用将二维码转为base64流-->
<dependency>
<groupId>org.apache.axis</groupId>
<artifactId>axis</artifactId>
<version>1.4</version>
</dependency>
第二步:工具类
public class QRCodeUtil {
// 生成二维码 content:二维码里面的内容 qrcodeVersion:决定着大小
public static BufferedImage encoderQRCoder(String content, int qrcodeVersion) {
try {
Qrcode handler = new Qrcode();
/*表示的字符串长度: 容错率(ECC) 显示编码模式(EncodeMode)及版本(Version)有关*/
/*二维码的纠错级别(排错率),共有四级:可选L(7%)、M(15%)、Q(25%)、H(30%)(最高H)。
纠错信息同样存储在二维码中,纠错级别越高,纠错信息占用的空间越多,那么能存储的有用信息就越少,对二维码清晰度的要求越小 */
handler.setQrcodeErrorCorrect('M');
//编码模式:Numeric 数字, Alphanumeric 英文字母,Binary 二进制,Kanji 汉字(第一个大写字母表示)
handler.setQrcodeEncodeMode('B');
handler.setQrcodeVersion(qrcodeVersion);
byte[] contentBytes = content.getBytes("UTF-8");
// 67 + 12 * (v - 1);
int size = 67 + 12 * (qrcodeVersion -1);
BufferedImage bufImg = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
Graphics2D gs = bufImg.createGraphics();
gs.setBackground(Color.WHITE);
gs.clearRect(0, 0, size, size);
//设定图像颜色:BLACK
gs.setColor(Color.BLACK);
//设置偏移量 不设置肯能导致解析出错
int pixoff = 2;
//输出内容:二维码
if(contentBytes.length > 0 && contentBytes.length < 1024) {
boolean[][] codeOut = handler.calQrcode(contentBytes);
for(int i = 0; i < codeOut.length; i++) {
for(int j = 0; j < codeOut.length; j++) {
if(codeOut[j][i]) {
gs.fillRect(j * 3 + pixoff, i * 3 + pixoff,3, 3);
}
}
}
} else {
System.err.println("QRCode content bytes length = " + contentBytes.length + " not in [ 0,120 ]. ");
}
gs.dispose();
bufImg.flush();
//生成二维码QRCode图片
return bufImg;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
第三步,应用,根据我的业务来,是需要在添加机具的方法里,调用生成二维码的方法,
//添加后
this.save(machine);
//移动端扫描需要的格式是这样的,所以就将这样的格式作为content放进二维码里
//{"type":1,“params”:{"machineId":111,"projcetId":222,"bidSectionId":333}}
Map<String,Object> map=new HashMap<>();
map.put("machineId", machine.getMachineId());//新添加的机具id
map.put("projectId",machine.getProjectId());//新添加的机具所在项目
map.put("bidSectionId",machine.getBidSectionId());//新添加的机具所在标段
//
JSONObject machineJson=new JSONObject();
machineJson.put("type",1); //考虑到后面有很多模块需要做二维码功能,所以拿type做区分,机具就是type为1
machineJson.put("params",map);
String machineString = machineJson.toJSONString();
// 生成二维码图片
BufferedImage bufferedImage = QRCodeUtil.encoderQRCoder(machineString,20);
String base64 = null;
try {
//输出流
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", stream);
base64 = Base64.encode(stream.toByteArray());
//机具表里存的有二维码字段,因为在添加前没有机具id,所以在save方法执行后拿到机具id再进行修改操作,
//此时只修二维码,之前为空字符串,所以此时的修改相当于添加了。之后将这个字段返回给前端,剩下的就是前端拿这个流去进行解析了
Machine m=new Machine();
m.setQrCode(String.format("%1$s%2$s","data:image/png;base64,",base64));
m.setMachineId(machine.getMachineId());
this.updateById(m);
} catch (Exception e) {
e.printStackTrace();
}
return true;
业务更改,不是添加的时候生成二维码,而是在查询的时候,前端传我一个机具id,点击查看二维码的时候调这个接口,将二维码和一些基本信息返回给他。
@ResponseBody
@RequestMapping(value = {"/createQrCode"}, method = RequestMethod.GET)
public JSONObject createQrCode(@RequestParam(value = "machineId")String machineId){
Machine machine = machineService.getById(machineId);
String format="";
if (machine.getEnterDate()!=null){
Timestamp enterDate = machine.getEnterDate();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
format = dateFormat.format(enterDate);
}else{
format ="/";
}
// 返回的信息
JSONObject jsonObject=new JSONObject();
jsonObject.put("machineName",machine.getMachineName());
jsonObject.put("machineNo",machine.getMachineNo());
jsonObject.put("specificationType",machine.getSpecificationType());
jsonObject.put("enterDate",format);
//二维码的内容
JSONObject qrCodeJson=new JSONObject();
qrCodeJson.put("type",1);
qrCodeJson.put("mId",machine.getMachineId());
qrCodeJson.put("pId",machine.getProjectId());
qrCodeJson.put("bId",machine.getBidSectionId());
String qrCodeString = qrCodeJson.toJSONString();
//二维码流
String base64Image = createImage(qrCodeString);
jsonObject.put("qrCode",String.format("%1$s%2$s","data:image/png;base64,",base64Image));
return ReturnUtil.success(jsonObject);
}
// 创建二维码并生成流
public static String createImage(String content) {
// 生成二维码图片
BufferedImage bufferedImage = QRCodeUtil.encoderQRCoder(content,5);
String base64 = null;
try {
//输出流
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", stream);
base64 = Base64.encode(stream.toByteArray());
} catch (Exception e) {
e.printStackTrace();
}
return base64;
}
ps :bufferedImage和base64的互转
https://www.2loveyou.com/articles/2020/01/19/1579401149903.html
批量导出二维码并生成压缩文件
/**
* 批量导出二维码
* @param machineIds
* @param response
*/
@ResponseBody
@RequestMapping(value = {"/importQrCode"}, method = RequestMethod.GET)
public void importQrCode(String machineIds, HttpServletResponse response) {
String path = "D://";
String pathName = "images";
// 先删除
// 先创建临时文件夹
File filePath = new File(path + pathName);
path = path + pathName;// path D://images
//如果文件夹存在 ,删除文件夹以及下面所有文件
if(filePath.exists()){
CompressUtil.deleteDir(path);
}
filePath.mkdir();//否则创建文件夹
// 压缩格式 这个需要在配置文件写
String format = "zip";
path = path + "/" ; // path D://images/
Map<String,String> map1 = new HashMap<>();
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String fileName = sdf.format(new Date());
// 先创建临时文件夹
File file = new File(path + fileName);
if(!file.exists()){//如果文件夹不存在
file.mkdir();//创建文件夹
}
// 根据拿到的ids从数据库查出集合
QueryWrapper<Machine> wrapper = Wrappers.query();
//前台传来字符串转“,”分隔的数组presentStatus:"1,2"转[1,2]
if (!StringUtil.isNullOrEmpty(machineIds)) {
wrapper.in("machineId",machineIds);
}
List<Map<String, Object>> machineList = machineService.listMaps(wrapper);
path = path + fileName; // D://images/2020-10-19
for (int i = 0; i < machineList.size(); i++) {
// 在machineList里生成文件
BASE64Decoder decoder =new BASE64Decoder();
//解码过程,即将base64字符串转换成二进制流
byte[] imageByte=decoder.decodeBuffer(machineList.get(i).get("qrCode") + "");
//生成图片路径和文件名
String imagePath = path + "/" + machineList.get(i).get("machineName") + ".jpg"; // D://images/2020-10-19/1.jpg
map1.put(machineList.get(i).get("machineName") + ".jpg",imagePath);
OutputStream out =new FileOutputStream(imagePath);
out.write(imageByte);
/*
* 使用流时,都会有一个缓冲区,按一种它认为比较高效的方法来发数据:
* 把要发的数据先放到缓冲区,缓冲区放满以后再一次性发过去,而不是分开一次一次地发.
* 而flush()表示强制将缓冲区中的数据发送出去,不必等到缓冲区满.
* 所以如果在用流的时候,没有用flush()这个方法,很多情况下会出
* 现流的另一边读不到数据的问题,特别是在数据特别小的情况下.
*/
out.flush();
out.close();
}
// 压缩且下载
CompressUtil.MultiFileZipDownload(response,(fileName + "." + format),map1);
} catch (Exception e) {
e.printStackTrace();
}
}
所需工具类:
/**
* 生成压缩文件 (zip,rar 格式)
*/
public class CompressUtil {
/**
* 多文件zip压缩下载 (说明:读取文件流到zip流中,之后下载)
* @param response
* @param downloadName 下载后的压缩包名,带后缀
* @param map 存放文件信息的map ,key为该文件压缩后的目录层级路径如:/xxx/xxx/xxx.jpg,为文件名时则在压缩包的顶层
* 如:xxx.jpg。 value为下载文件所在的路径
*/
public static void MultiFileZipDownload(HttpServletResponse response, String downloadName, Map<String,String> map) {
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
ZipOutputStream zos=new ZipOutputStream(outputStream);
try {
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(downloadName, "UTF-8"));
map.forEach((name,path)->{
InputStream is = null;
BufferedInputStream in = null;
byte[] buffer = new byte[1024];
int len;
//创建zip实体(一个文件对应一个ZipEntry)
//name --->压缩包的层级路径
ZipEntry entry = new ZipEntry(name);
try {
//获取需要下载的文件流
File file=new File(path);
is = new FileInputStream(file);
in = new BufferedInputStream(is);
zos.putNextEntry(entry);
//文件流循环写入ZipOutputStream
while ((len = in.read(buffer)) != -1 ) {
zos.write(buffer, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(entry != null) {
try {
zos.closeEntry();
} catch (Exception e) {
e.printStackTrace();
}
}
if(in != null) {
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(is != null) {
try {
is.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
});
} catch (IOException e) {
e.printStackTrace();
}finally {
if(zos != null) {
try {
zos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(outputStream != null) {
try {
outputStream.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/**
* @param path 要压缩的文件路径
* @param format 生成的格式(zip、rar)d
*/
public static void generateFile(String path, String format) throws Exception {
File file = new File(path);
// 压缩文件的路径不存在
if (!file.exists()) {
throw new Exception("路径 " + path + " 不存在文件,无法进行压缩...");
}
// 用于存放压缩文件的文件夹
String generateFile = file.getParent();
File compress = new File(generateFile);
// 如果文件夹不存在,进行创建
if( !compress.exists() ){
compress.mkdirs();
}
// 目的压缩文件
String generateFileName = compress.getAbsolutePath() + File.separator + file.getName() + "." + format;
// 输入流 表示从一个源读取数据
// 输出流 表示向一个目标写入数据
// 输出流
FileOutputStream outputStream = new FileOutputStream(generateFileName);
// 压缩输出流
ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream));
generateFile(zipOutputStream,file,"");
System.out.println("源文件位置:" + file.getAbsolutePath() + ",目的压缩文件生成位置:" + generateFileName);
// 关闭 输出流
zipOutputStream.close();
}
/**
* @param out 输出流
* @param file 目标文件
* @param dir 文件夹
* @throws Exception
*/
private static void generateFile(ZipOutputStream out, File file, String dir) throws Exception {
// 当前的是文件夹,则进行一步处理
if (file.isDirectory()) {
//得到文件列表信息
File[] files = file.listFiles();
//将文件夹添加到下一级打包目录
out.putNextEntry(new ZipEntry(dir + "/"));
dir = dir.length() == 0 ? "" : dir + "/";
//循环将文件夹中的文件打包
for (int i = 0; i < files.length; i++) {
generateFile(out, files[i], dir + files[i].getName());
}
} else { // 当前是文件
// 输入流
FileInputStream inputStream = new FileInputStream(file);
// 标记要打包的条目
out.putNextEntry(new ZipEntry(dir));
// 进行写操作
int len = 0;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes)) > 0) {
out.write(bytes, 0, len);
}
// 关闭输入流
inputStream.close();
}
}
/**
* 迭代删除文件夹
* @param dirPath 文件夹路径
*/
public static void deleteDir(String dirPath)
{
File file = new File(dirPath);
if(file.isFile())
{
file.delete();
}else
{
File[] files = file.listFiles();
if(files == null)
{
file.delete();
}else
{
for (int i = 0; i < files.length; i++)
{
deleteDir(files[i].getAbsolutePath());
}
file.delete();
}
}
}
}