1、首先在maven中加入生成二維碼的一個依賴包:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing-core.version}</version>
</dependency>
這裏我用的是3.3.0版本
<zxing-core.version>3.3.0</zxing-core.version>
2、所需的一個工具類QRCodeUtils
import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.util.Assert;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Objects;
public class QRCodeUtils {
private static final String CHARSET = "utf-8";
private static final String FORMAT_NAME = "JPG";
// 二維碼尺寸
private static final int QRCODE_SIZE = 750;
// LOGO寬度
private static final int WIDTH = 250;
// LOGO高度
private static final int HEIGHT = 250;
/********************************************************************************
*
*
*
*********************************************************************************/
public static void encode(String content, Image src, boolean needCompress, OutputStream output) {
BufferedImage image = createImage(content);
// 插入圖片
if(Objects.nonNull(src)){
insertImage(image, src, needCompress);
}
try {
ImageIO.write(image, FORMAT_NAME, output);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, InputStream inputStream, boolean needCompress, OutputStream output) {
try {
encode(content, ImageIO.read(inputStream), needCompress, output);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, File file, boolean needCompress, OutputStream output) {
try {
encode(content, ImageIO.read(file), needCompress, output);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, String imgPath, boolean needCompress, OutputStream output) {
File file = new File(imgPath);
Assert.isTrue(file.exists(), MessageFormat.format("文件:{0} 不存在",imgPath));
encode(content, file, needCompress, output);
}
public static void encode(String content, Image src, boolean needCompress, File destFile) {
BufferedImage image = createImage(content);
// 插入圖片
if(Objects.nonNull(src)){
insertImage(image, src, needCompress);
}
if (!destFile.exists() && !destFile.isDirectory()) {
destFile.mkdirs();
}
try {
ImageIO.write(image, FORMAT_NAME, destFile);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, InputStream inputStream, boolean needCompress, File destFile) {
try {
encode(content, ImageIO.read(inputStream), needCompress, destFile);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, File file, boolean needCompress, File destFile) {
try {
encode(content, ImageIO.read(file), needCompress, destFile);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, String imgPath, boolean needCompress, File destFile) {
File file = new File(imgPath);
Assert.isTrue(file.exists(), MessageFormat.format("文件:{0} 不存在",imgPath));
encode(content, file, needCompress, destFile);
}
public static void encode(String content, Image src, boolean needCompress, String destPath) {
encode(content, src, needCompress, new File(destPath));
}
public static void encode(String content, InputStream inputStream, boolean needCompress, String destPath) {
try {
encode(content, ImageIO.read(inputStream), needCompress, destPath);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, File file, boolean needCompress, String destPath) {
Assert.isTrue(file.exists(), MessageFormat.format("文件:{0} 不存在",file.getAbsolutePath()));
try {
encode(content, ImageIO.read(file), needCompress, destPath);
} catch (IOException e) {
throw new BusinessException(e);
}
}
public static void encode(String content, String imgPath, boolean needCompress, String destPath) {
File file = new File(imgPath);
encode(content, file, needCompress, destPath);
}
public static void encode(String content, Image src, OutputStream output) {
encode(content, src, true, output);
}
public static void encode(String content, InputStream inputStream, OutputStream output) {
encode(content, inputStream, true, output);
}
public static void encode(String content, File file, OutputStream output) {
encode(content, file, true, output);
}
public static void encode(String content, String imgPath, OutputStream output) {
encode(content, imgPath, true, output);
}
public static void encode(String content, Image src, File destFile) {
encode(content, src, true, destFile);
}
public static void encode(String content, InputStream inputStream, File destFile) {
encode(content, inputStream, true, destFile);
}
public static void encode(String content, File file, File destFile) {
encode(content, file, true, destFile);
}
public static void encode(String content, String imgPath, File destFile) {
encode(content, imgPath, true, destFile);
}
public static void encode(String content, Image src, String destPath) {
encode(content, src, true, destPath);
}
public static void encode(String content, InputStream inputStream, String destPath) {
encode(content, inputStream, true, destPath);
}
public static void encode(String content, File file, String destPath) {
encode(content, file, true, destPath);
}
public static void encode(String content, String imgPath, String destPath) {
encode(content, imgPath, true, destPath);
}
/********************************************************************************
*
*
*
*********************************************************************************/
public static String decode(BufferedImage image) {
if (image == null) {
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
Hashtable hints = new Hashtable();
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
try {
result = new MultiFormatReader().decode(bitmap, hints);
} catch (NotFoundException e) {
throw new BusinessException(e);
}
String resultStr = result.getText();
return resultStr;
}
public static String decode(InputStream inputStream){
BufferedImage image = null;
try {
image = ImageIO.read(inputStream);
} catch (IOException e) {
throw new BusinessException(e);
}
return decode(image);
}
public static String decode(File file){
BufferedImage image = null;
try {
image = ImageIO.read(file);
} catch (IOException e) {
throw new BusinessException(e);
}
return decode(image);
}
public static String decode(String path){
return decode(new File(path));
}
private static BufferedImage createImage(String content) {
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = null;
try {
bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
} catch (WriterException e) {
throw new BusinessException(e);
}
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
return image;
}
private static void insertImage(BufferedImage source, Image src, boolean needCompress){
int width = src.getWidth(null);
int height = src.getHeight(null);
if (needCompress) { // 壓縮LOGO
//if (width > WIDTH) {
width = WIDTH;
//}
//if (height > HEIGHT) {
height = HEIGHT;
//}
Image image = src.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null); // 繪製縮小後的圖
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE_SIZE - width) / 2;
int y = (QRCODE_SIZE - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
static class BufferedImageLuminanceSource extends LuminanceSource {
private final BufferedImage image;
private final int left;
private final int top;
public BufferedImageLuminanceSource(BufferedImage image) {
this(image, 0, 0, image.getWidth(), image.getHeight());
}
public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {
super(width, height);
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
if (left + width > sourceWidth || top + height > sourceHeight) {
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
}
for (int y = top; y < top + height; y++) {
for (int x = left; x < left + width; x++) {
if ((image.getRGB(x, y) & 0xFF000000) == 0) {
image.setRGB(x, y, 0xFFFFFFFF); // = white
}
}
}
this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);
this.image.getGraphics().drawImage(image, 0, 0, null);
this.left = left;
this.top = top;
}
@Override
public byte[] getRow(int y, byte[] row) {
if (y < 0 || y >= getHeight()) {
throw new IllegalArgumentException("Requested row is outside the image: " + y);
}
int width = getWidth();
if (row == null || row.length < width) {
row = new byte[width];
}
image.getRaster().getDataElements(left, top + y, width, 1, row);
return row;
}
@Override
public byte[] getMatrix() {
int width = getWidth();
int height = getHeight();
int area = width * height;
byte[] matrix = new byte[area];
image.getRaster().getDataElements(left, top, width, height, matrix);
return matrix;
}
@Override
public boolean isCropSupported() {
return true;
}
@Override
public LuminanceSource crop(int left, int top, int width, int height) {
return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);
}
@Override
public boolean isRotateSupported() {
return true;
}
@Override
public LuminanceSource rotateCounterClockwise() {
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);
BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = rotatedImage.createGraphics();
g.drawImage(image, transform, null);
g.dispose();
int width = getWidth();
return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
}
}
}
3、創建配置文件、並將二維碼中間的圖片命名爲qr-code-center.png
放到resource
目錄下的file
文件夾下
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* QRCodeProperty
*
* @desc: TODO 類的設計目的、功能及注意事項
* @version:
* @createTime: 2020/5/7 15:53
* @author:
*/
@Component
@ConfigurationProperties(prefix = "qr-code")
@Getter
@Setter
public class QRCodeProperty {
/**
* 二維碼解析地址,一般要配置成公網可訪問的二維碼解析地址
*/
private String parseUrl;
/**
* 二維碼是否需要壓縮
*/
private boolean needCompress;
}
qr-code:
parse-url: http://localhost:8980/api/appVersion/qrCode
need-compress: true
4、創建實體類AppVersion.java
,及其AppVersionMapper.java
、IAppVersionService.java
、AppVersionServiceImpl.java
、AppVersionApi.java
、AppVersionMapper.xml
下面僅展示AppVersion.java
、AppVersionServiceImpl.java
、AppVersionApi.java
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* AppVersion
*
* @desc: TODO 類的設計目的、功能及注意事項
* @version:
* @createTime: 2020/5/7 14:14
* @author:
*/
@Getter
@Setter
@Accessors(chain = true)
public class AppVersion {
/**
* 主鍵
*/
private Long id;
/**
* 更新時間
*/
private Date updateTime;
/**
* 更新內容
*/
private String updateContent;
/**
* app版本號,設定爲不可編輯
*/
private String appVersion;
/**
* apk文件大小
*/
private Long appSize;
/**
* apk文件下載地址
*/
private String downloadUrl;
/**
* 二維碼地址
*/
private String qrCodeUrl;
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.text.MessageFormat;
import java.util.Objects;
import java.util.UUID;
/**
* AppVersionServiceImpl
*
* @desc: TODO 類的設計目的、功能及注意事項
* @version:
* @createTime: 2020/5/7 14:29
* @author:
*/
@Service
public class AppVersionServiceImpl implements IAppVersionService {
private final static String BASE_APP_FOLDER = "app";
@Autowired
private IFileService fileService;
@Autowired
private AppVersionMapper appVersionMapper;
@Autowired
private QRCodeProperty qrCodeProperty;
/**
* getFolder
* @desc: apk文件、二維碼存放文件夾
* @author:
* @createTime: 2020/5/7 14:41
* @param appVersion
* @return: java.lang.String
*/
private static String getFolder(AppVersion appVersion){
return BASE_APP_FOLDER + File.separator + appVersion.getAppVersion();
}
@Override
public AppVersion selectById(long id) {
return appVersionMapper.selectById(id);
}
@Override
public AppVersion selectLast() {
return appVersionMapper.selectLast();
}
@Override
public long insert(AppVersion appVersion, MultipartFile multipartFile) {
//TODO 1、先檢查版本號是否已存在、略......
//2、存儲上傳的apk文件,並設置appSize、downloadUrl
String folder = getFolder(appVersion);
UploadResult uploadResult = fileService.upload(multipartFile, folder);
appVersion.setAppSize(uploadResult.getFileSize()).setDownloadUrl(uploadResult.getDownloadUrl());
//3、設置qrCodeUrl、保存appVersion、生成二維碼
String qrCodePath = folder + File.separator + UUID.randomUUID().toString().replaceAll("-","") + ".png";
appVersion.setQrCodeUrl(FileUtils.getDownloadUrl(qrCodePath));
appVersionMapper.insert(appVersion);
QRCodeUtils.encode(
MessageFormat.format("{0}?id={1}", qrCodeProperty.getParseUrl(), appVersion.getId()),
AppVersionServiceImpl.class.getClassLoader().getResourceAsStream("file/qr-code-center.png"),
qrCodeProperty.isNeedCompress(),
fileService.getAbsolutePath(qrCodePath)
);
return appVersion.getId();
}
@Override
public int update(AppVersion appVersion, MultipartFile multipartFile) {
//1、如果更新了apk文件則:
if(Objects.nonNull(multipartFile)){
String folder = getFolder(appVersion);
UploadResult uploadResult = fileService.upload(multipartFile, folder);
appVersion.setAppSize(uploadResult.getFileSize()).setDownloadUrl(uploadResult.getDownloadUrl());
//此時無需再重新生成二維碼
}
return appVersionMapper.update(appVersion);
}
@Override
public int delete(long id) {
return appVersionMapper.delete(id);
}
}
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.Objects;
/**
* AppVersionApi
*
* @desc: TODO 類的設計目的、功能及注意事項
* @version:
* @createTime: 2020/5/7 16:25
* @author:
*/
@Controller
@RequestMapping("/api/appVersion")
public class AppVersionApi {
@Autowired
private IAppVersionService appVersionService;
@PostMapping("/insert")
@ResponseBody
public AjaxResult<Long> insert(
@RequestParam("file") MultipartFile file,
@RequestParam("appVersion") String _appVersion
) {
AppVersion appVersion = JSON.parseObject(_appVersion,AppVersion.class);
return AjaxResult.success(appVersionService.insert(appVersion, file));
}
@PostMapping("/update")
@ResponseBody
public AjaxResult<Integer> update(
@RequestParam(value = "file", required = false) MultipartFile file,
@RequestParam("appVersion") String _appVersion
) {
AppVersion appVersion = JSON.parseObject(_appVersion,AppVersion.class);
return AjaxResult.success(appVersionService.update(appVersion, file));
}
@PostMapping("/delete")
@ResponseBody
public AjaxResult<Integer> delete(long id) {
int res = appVersionService.delete(id);
return AjaxResult.success(res);
}
@GetMapping("/qrCode")
public String qrCode(@RequestParam(value="id", required = false) Long id, Model model){
AppVersion appVersion = Objects.isNull(id) ? appVersionService.selectLast() : appVersionService.selectById(id);
String title, error = null;
if(Objects.isNull(appVersion)){
title = "錯誤";
error = "此二維碼鏈接已失效";
}else {
title = "下載";
}
model.addAttribute("title", title);
model.addAttribute("error", error);
model.addAttribute("appVersion", appVersion);
return "qrCode";
}
}
至於qrCode
頁面就自己設計了
備註:上面使用的IFileService、FileUtils
均在之前的博客springboot2.2.6文件上傳、下載及文件超出大小限制的處理能找到