当前位置: 首页 > news >正文

二维码生成器

这是一个基于Java实现的二维码生成器项目,使用SpringBoot框架和HTML页面构建。核心功能包括:通过ZXing库生成二维码、支持自定义尺寸和添加Logo、提供文件下载功能。项目采用JDK1.8开发,使用Thymeleaf进行页面渲染,代码结构清晰,包含控制层处理页面渲染、文件上传和二维码生成逻辑。主要特性是能够根据用户输入的文本内容生成带Logo的可下载二维码图片文件。

一、效果展示

在这里插入图片描述

二、代码说明

  • jdk1.8
  • 使用springBoot框架+html
  • 使用Thymeleaf实现页面渲染
  • 使用zxing实现二维码的生成渲染

三、目录结构

在这里插入图片描述

四、代码展示

  • QrCodeController:控制层,实现页面渲染、文件上传、二维码生成
package com.qrcode.controller;import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;@Controller
public class QrCodeController {@Value("${qrcode.save.path}")private String qrCodeSavePath;/*** 加载index.html页面*/@GetMapping("/")public String index(Model model) {model.addAttribute("message", "欢迎使用二维码生成器");return "index.html";}/*** 生成二维码*/@PostMapping("/generate")public String generateQrCode(@RequestParam("content") String content,@RequestParam(value = "width", defaultValue = "300") int width,@RequestParam(value = "height", defaultValue = "300") int height,@RequestParam(value = "logo", required = false) MultipartFile logo,Model model) {try {// 验证输入if (content == null || content.trim().isEmpty()) {model.addAttribute("message", "请输入二维码内容");return "index.html";}// 创建保存目录File saveDir = new File(qrCodeSavePath);if (!saveDir.exists()) {boolean created = saveDir.mkdirs();if (!created) {model.addAttribute("message", "无法创建保存目录");return "index.html";}}// 生成文件名String fileName = "qrcode_" + System.currentTimeMillis() + ".png";File qrCodeFile = new File(saveDir, fileName);// 生成二维码Map<EncodeHintType, Object> hints = new HashMap<>();hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");hints.put(EncodeHintType.MARGIN, 1);BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);// 将二维码写入文件MatrixToImageWriter.writeToPath(matrix, "PNG", qrCodeFile.toPath());// 如果上传了Logo,添加Logo到二维码if (logo != null && !logo.isEmpty()) {addLogoToQRCode(qrCodeFile, logo);}model.addAttribute("message", "二维码生成成功!");model.addAttribute("qrCodeUrl", "/download/" + fileName);} catch (Exception e) {model.addAttribute("message", "二维码生成失败: " + e.getMessage());e.printStackTrace();}return "index.html";}/*** 文件上传*/@GetMapping("/download/{fileName:.+}")public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {try {Path filePath = Paths.get(qrCodeSavePath).resolve(fileName).normalize();Resource resource = new UrlResource(filePath.toUri());if (resource.exists() && resource.isReadable()) {return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=\"" + resource.getFilename() + "\"").contentType(MediaType.IMAGE_PNG).body(resource);} else {return ResponseEntity.notFound().build();}} catch (Exception e) {return ResponseEntity.internalServerError().build();}}/*** 添加logo*/private void addLogoToQRCode(File qrCodeFile, MultipartFile logo) throws IOException {try {// 读取原始二维码BufferedImage qrImage = ImageIO.read(qrCodeFile);if (qrImage == null) {throw new IOException("无法读取二维码文件");}// 读取LogoBufferedImage logoImage = ImageIO.read(new ByteArrayInputStream(logo.getBytes()));if (logoImage == null) {throw new IOException("无法读取Logo文件");}// 计算Logo尺寸(二维码的1/5)int logoWidth = Math.min(qrImage.getWidth() / 5, logoImage.getWidth());int logoHeight = Math.min(qrImage.getHeight() / 5, logoImage.getHeight());// 调整Logo大小BufferedImage scaledLogo = new BufferedImage(logoWidth, logoHeight, BufferedImage.TYPE_INT_ARGB);Graphics2D g = scaledLogo.createGraphics();g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g.drawImage(logoImage, 0, 0, logoWidth, logoHeight, null);g.dispose();// 计算Logo位置(居中)int x = (qrImage.getWidth() - logoWidth) / 2;int y = (qrImage.getHeight() - logoHeight) / 2;// 将Logo绘制到二维码上Graphics2D graphics = qrImage.createGraphics();graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);graphics.drawImage(scaledLogo, x, y, null);graphics.dispose();// 保存带Logo的二维码boolean success = ImageIO.write(qrImage, "PNG", qrCodeFile);if (!success) {throw new IOException("无法保存带Logo的二维码");}} catch (Exception e) {throw new IOException("添加Logo失败: " + e.getMessage(), e);}}
}
  • BrowserLauncher:实现运行成功后自动打开http://localhost:8080/路径
package com.qrcode.utils;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;import java.awt.*;
import java.net.URI;@Component
public class BrowserLauncher {@Value("${server.port}")private String PORT;@EventListener(ApplicationReadyEvent.class)public void launchBrowser() {try {Thread.sleep(1000);// 增强的环境检测if (isGraphicalEnvironmentAvailable()) {Desktop desktop = Desktop.getDesktop();if (desktop.isSupported(Desktop.Action.BROWSE)) {desktop.browse(new URI("http://localhost:" + PORT));System.out.println("浏览器已自动打开");return;}}// 如果桌面环境不支持,尝试命令行方式openBrowserWithCommand();} catch (Exception e) {System.out.println("无法自动打开浏览器: " + e.getMessage());}}private boolean isGraphicalEnvironmentAvailable() {try {// 检查是否有显示器if (System.getenv("DISPLAY") == null && !System.getProperty("os.name").toLowerCase().contains("win")) {return false;}// 检查Desktop类是否可用return Desktop.isDesktopSupported();} catch (Exception e) {return false;}}private void openBrowserWithCommand() {try {String os = System.getProperty("os.name").toLowerCase();Runtime runtime = Runtime.getRuntime();if (os.contains("win")) {// Windowsruntime.exec("cmd /c start http://localhost:" + PORT);} else if (os.contains("mac")) {// macOSruntime.exec(new String[]{"open", "http://localhost:" + PORT});} else if (os.contains("nix") || os.contains("nux")) {// Linux/Unixruntime.exec(new String[]{"xdg-open", "http://localhost:" + PORT});}System.out.println("已尝试通过命令行打开浏览器");} catch (Exception e) {System.out.println("命令行打开浏览器也失败了");}}
}
  • index.html:前端页面
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>二维码生成器</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css"><style>body {background: linear-gradient(135deg, #f5f7fa 0%, #e4e8f0 100%);min-height: 100vh;padding: 20px 0;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;}.card {border: none;border-radius: 12px;overflow: hidden;box-shadow: 0 8px 25px rgba(0, 0, 0, 0.09);}.card-header {padding: 20px;border-bottom: 1px solid rgba(0, 0, 0, 0.05);}.card-body {padding: 30px;}.form-control, .form-control:focus {border-radius: 8px;padding: 12px 15px;box-shadow: none;border: 1px solid #dce1e8;transition: all 0.3s;}.form-control:focus {border-color: #4b6cb7;box-shadow: 0 0 0 3px rgba(75, 108, 183, 0.15);}.form-label {font-weight: 500;margin-bottom: 8px;color: #3a4a6b;}.btn-primary {background: #4b6cb7;border: none;padding: 12px;border-radius: 8px;font-weight: 600;transition: all 0.3s;}.btn-primary:hover {background: #3a5aa0;transform: translateY(-2px);}.btn-success {background: #2ecc71;border: none;border-radius: 8px;padding: 10px 20px;transition: all 0.3s;}.btn-success:hover {background: #27ae60;transform: translateY(-2px);}.preview-area {background: #f8f9fa;border-radius: 12px;padding: 25px;transition: all 0.3s;}.header-icon {font-size: 24px;margin-right: 10px;}.feature-list {font-size: 0.9rem;color: #6c757d;text-align: center;margin-bottom: 20px;}.upload-info {font-size: 0.85rem;color: #6c757d;margin-top: 5px;}.btn {height: 50px;}</style>
</head>
<body>
<div class="container mt-4"><div class="row justify-content-center"><div class="col-lg-8"><div class="card shadow"><div class="card-header bg-primary text-white"><h2 class="text-center mb-0"><i class="bi bi-qr-code header-icon"></i>二维码生成器</h2></div><div class="card-body"><!-- 消息显示 --><div th:if="${message}" class="alert alert-info alert-dismissible fade show" th:text="${message}"><button type="button" class="btn-close" data-bs-dismiss="alert"></button></div><!-- 功能特点 --><p class="feature-list">快速生成二维码 • 自定义尺寸 • 支持添加Logo</p><!-- 生成表单 --><form method="post" action="/generate" enctype="multipart/form-data"><div class="mb-4"><label class="form-label">二维码内容 <span class="text-danger">*</span></label><input type="text" class="form-control" name="content" requiredplaceholder="输入网址或文本,例如:https://www.example.com"></div><div class="row"><div class="col-md-6 mb-4"><label class="form-label">宽度 (像素)</label><input type="number" class="form-control" name="width" value="300" min="100" max="1000"></div><div class="col-md-6 mb-4"><label class="form-label">高度 (像素)</label><input type="number" class="form-control" name="height" value="300" min="100" max="1000"></div></div><div class="mb-4"><label class="form-label">Logo (可选)</label><input type="file" class="form-control" name="logo" accept="image/*"><div class="upload-info">建议使用正方形透明背景的PNG图片</div></div><button type="submit" class="btn btn-primary w-100 py-2"><i class="bi bi-qr-code me-2"></i>生成二维码</button></form><!-- 预览区域 --><div th:if="${qrCodeUrl}" class="mt-5 preview-area"><h5 class="text-center mb-4"><i class="bi bi-image me-2"></i>生成的二维码</h5><div class="d-flex justify-content-center"><img th:src="${qrCodeUrl}" alt="生成的二维码" class="img-fluid mb-4" style="max-width: 300px; border-radius: 8px;"></div><div class="text-center"><a th:href="${qrCodeUrl}" download class="btn btn-success"><i class="bi bi-download me-2"></i>下载二维码</a></div></div></div></div><!-- 页脚 --><div class="text-center mt-4 text-muted"><small>© Demo侠 二维码生成工具</small></div></div></div>
</div><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
  • application.properties:配置文件
# 端口
server.port=8080
server.servlet.context-path=/# 文件上传
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.file-size-threshold=2MB# Thymeleaf模版设置
spring.thymeleaf.prefix=classpath:/static/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.cache=false# 文件上传路径
qrcode.save.path=./qrcodes# 日志
logging.level.com.example.qrgenerator=DEBUG
logging.level.org.springframework.web=INFO

文章转载自:

http://XtGjIPFt.bpxmr.cn
http://E2qSo7XV.bpxmr.cn
http://Fr9fPGSz.bpxmr.cn
http://FWjq9Zra.bpxmr.cn
http://dO3MbJW8.bpxmr.cn
http://vgjfZ3eq.bpxmr.cn
http://SWqxSga6.bpxmr.cn
http://bF2T0Vxj.bpxmr.cn
http://1zLCgg0P.bpxmr.cn
http://hBCwwqp7.bpxmr.cn
http://eKQOB9l5.bpxmr.cn
http://kbV8zTIL.bpxmr.cn
http://qWTmFXuh.bpxmr.cn
http://Vpe2M8TK.bpxmr.cn
http://rogdpJSN.bpxmr.cn
http://G2EyQDVr.bpxmr.cn
http://pCcx5o4M.bpxmr.cn
http://WYrnHnNn.bpxmr.cn
http://vVCtg4ub.bpxmr.cn
http://ugm3PXGC.bpxmr.cn
http://WbnEjohc.bpxmr.cn
http://g7wCqBfX.bpxmr.cn
http://PXmaRN3G.bpxmr.cn
http://EGjSlkXV.bpxmr.cn
http://DF9ymUGS.bpxmr.cn
http://JVmUE48o.bpxmr.cn
http://IQ6n1B7t.bpxmr.cn
http://kbZYOr4I.bpxmr.cn
http://T5WejCgI.bpxmr.cn
http://qDb6484b.bpxmr.cn
http://www.dtcms.com/a/386620.html

相关文章:

  • OSI七层模型
  • 【原创·极简新视角剖析】【组局域网】设备在同一局域网的2个条件
  • 第8课:高级检索技术:HyDE与RAG-Fusion原理与DeepSeek实战
  • Windows 命令行:路径的概念,绝对路径
  • 异常检测在网络安全中的应用
  • 【ubuntu】ubuntu 22.04 虚拟机中扩容操作
  • 【数值分析】05-绪论-章节课后1-7习题及答案
  • Java NIO 核心机制与应用
  • Roo Code 诊断集成功能:智能识别与修复代码问题
  • ANA Pay不再接受海外信用卡储值 日eShop生路再断一条
  • 一阶惯性环节的迭代公式
  • AWS 热门服务(2025 年版)
  • 拷打字节算法面试官之-深入c语言递归算法
  • Vehiclehal的VehicleService.cpp
  • 【传奇开心果系列】基于Flet框架实现的允许调整大小的开关自定义组件customswitch示例模板特色和实现原理深度解析
  • 八股整理xdsm
  • SpringBoot 配置文件详解:从基础语法到实战应用
  • lesson62:JavaScript对象进化:ES2025新特性深度解析与实战指南
  • ARM C1-Premium core简介
  • 机器学习-深度神经网络架构
  • godot+c#实现玩家动画
  • 【Axure高保真原型】标签树分类查询案例
  • 系统架构设计(一)
  • RK3568下QT实简易文件浏览器
  • 设备综合效率(OEE)讲解与计算案例
  • STM32G4 电流环闭环(二) 霍尔有感运行
  • git-gui --批量处理文件
  • 【代码随想录day 28】 力扣 55.跳跃游戏
  • Python Flask 项目实战
  • whisper.cpp参数调优