Spring 使用<img>展示二维码标签

本文介绍如何在Spring环境中生成带有或不带Logo的二维码,并提供在线展示及下载功能。

前言

本文实现 Spring 环境前端访问 <img>,访问后台,展示出一个二维码

出于简化的考虑,本文的二维码在后台固定内容,大小等参数。

 

1、之前写过一篇文章:关于二维码生成的3种方式

但是其中关于在线生成二维码的方式不是Spring的,所以写本文算是以后可以直接使用代码了

http://blog.youkuaiyun.com/bestcxx/article/details/53116996

 

2、Spring 基本环境搭建

(MAVEN)Spring MVC 入门之页面跳转

 

3、pom.xml 增加zxing依赖

 

 <!-- 字符串生成二维码 -->
	    <dependency>
		    <groupId>com.google.zxing</groupId>
		    <artifactId>core</artifactId>
		    <version>3.3.0</version>
		</dependency>


4、前台页面

 

logoFlag=yes 则二维码会额外增加logo,logo是20乘以20

 

<br><br>
	<img alt="纯二维码测试" src="<%=basePath %>/qrCode/qrCode?logoFlag=no">
	<br><br>
	<img alt="二维码增加图标测试" src="<%=basePath %>/qrCode/qrCode?logoFlag=yes">

 

 

 

 

 

5、后台Controller

 

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;

@Controller
@RequestMapping("/qrCode")
public class RqCodeController {
	
	/**
	 * 
	 * @param request
	 * @param response
	 * @param logoFlag  是否需要为二维码增加logo  yes:增加,no:不增加
	 * @throws IOException
	 */
	@GetMapping("/qrCode")
	public void getQrCode(HttpServletRequest request,HttpServletResponse response,String logoFlag) throws IOException{
		response.setContentType("image/jpeg");
		OutputStream out = response.getOutputStream();
		response.setHeader("Pragma","No-cache"); 
		response.setHeader("Cache-Control","no-cache"); 
		response.setDateHeader("Expires", 0);
		
		 String url="http://www.bestcxx.cn";
		 int width = 180;   
		 int height = 180;
		 String format = "png";
		 String logoPath="D://1.PNG";//logo的地址
	     Hashtable hints= new Hashtable();   
	     hints.put(EncodeHintType.CHARACTER_SET, "utf-8");   
	     BitMatrix bitMatrix; 
	     
	     try {
				
				bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, width, height,hints);
				//去白边
				int[] rec = bitMatrix.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 (bitMatrix.get(i + rec[0], j + rec[1])) { 
		     	             resMatrix.set(i, j); 
		     	        } 
		     	    }  
		     	}
		     	BufferedImage image = toBufferedImage(resMatrix);
		     	
		     	//增加图标
		     	if("yes".equals(logoFlag)){//标志位为yes 则增加图标
					BufferedImage logo = ImageIO.read(new File(logoPath));
					Graphics2D g = image.createGraphics();
					// logo宽高
					width =20;
					height =20;
					
					// logo起始位置,此目的是为logo居中显示
					int x = (image.getWidth() - width) / 2;
					int y = (image.getHeight() - height) / 2;
					g.drawImage(logo, x, y, null);
					g.dispose();
		     	}
		     	
		     	ImageIO.write(image, "PNG", out); 

			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}   
	}
	
	
	
	 public static BufferedImage toBufferedImage(BitMatrix matrix) {
			int BLACK = 0xFF000000;
			int WHITE = 0xFFFFFFFF;
			int width = matrix.getWidth();
			int height = matrix.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, matrix.get(x, y) ? BLACK : WHITE);
			 	}
			}
			return image;
		}

}


6、效果

 

 

 

+2017-11-28+

我们可以将这个方法优化为工具类,不直接对外开放,有修改,这样可以提供二维码在线展示或者下载

 

import java.awt.Graphics2D;  
import java.awt.image.BufferedImage;  
import java.io.File;  
import java.io.IOException;  
import java.io.OutputStream;
import java.util.Date;
import java.util.Hashtable;  
  
import javax.imageio.ImageIO;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
import com.google.zxing.BarcodeFormat;  
import com.google.zxing.EncodeHintType;  
import com.google.zxing.MultiFormatWriter;  
import com.google.zxing.common.BitMatrix;

/**
 * 生成二维码的工具类
 * @author Administrator
 *
 */
public class RqCodeUtil {

	/** 
     *  
     * @param request 
     * @param response 
     * @param logoFlag  是否需要为二维码增加logo  yes:增加,no:不增加 
     * @param typeFlag  1-图片下载 ,2-图片在线展示
     * @param fielName  图片名称
     * @throws IOException 
     */  
    public void getQrCode(HttpServletRequest request,HttpServletResponse response,
    		String logoFlag,String url,String typeFlag,String fielName) throws IOException{  
        if("1".equals(typeFlag)){
        	response.setContentType("application/octet-stream"); //图片下载        	
        }else{
        	response.setContentType("image/jpeg"); //图片展示
        }
        
        OutputStream out = response.getOutputStream();  
        response.setHeader("Pragma","No-cache");   
        response.setHeader("Cache-Control","no-cache");   
        response.setDateHeader("Expires", 0);  
        response.addHeader("Content-Disposition","attachment;filename="+new String((fielName+new Date().getTime()+".png").getBytes()));//设置header,文件名字  
       
	    int width = 180;     
	    int height = 180;  
	    String format = "png";  
	    String logoPath="D://1.PNG";//logo的地址  
	    Hashtable hints= new Hashtable();     
	    hints.put(EncodeHintType.CHARACTER_SET, "utf-8");     
	    BitMatrix bitMatrix;   
           
        try {  
                  
            bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, width, height,hints);  
            //去白边  
            int[] rec = bitMatrix.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 (bitMatrix.get(i + rec[0], j + rec[1])) {   
                         resMatrix.set(i, j);   
                    }   
                }    
            }  
            BufferedImage image = toBufferedImage(resMatrix);  
              
            //增加图标  
            if("yes".equals(logoFlag)){//标志位为yes 则增加图标  
                BufferedImage logo = ImageIO.read(new File(logoPath));  
                Graphics2D g = image.createGraphics();  
                // logo宽高  
                width =20;  
                height =20;  
                  
                // logo起始位置,此目的是为logo居中显示  
                int x = (image.getWidth() - width) / 2;  
                int y = (image.getHeight() - height) / 2;  
                g.drawImage(logo, x, y, null);  
                g.dispose();  
            }  
              
            ImageIO.write(image, "PNG", out);   
  
            } catch (Exception e) {  
                // TODO Auto-generated catch block  
            e.printStackTrace();  
        }     
    }  
     
     public static BufferedImage toBufferedImage(BitMatrix matrix) {  
        int BLACK = 0xFF000000;  
        int WHITE = 0xFFFFFFFF;  
        int width = matrix.getWidth();  
        int height = matrix.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, matrix.get(x, y) ? BLACK : WHITE);  
            }  
        }  
        return image;  
     }  
  
}  

+20190313如果要指定图片名称+

response.setHeader("Content-Disposition", "attachment;filename=名字.jpg");

 

 

 

 

 

 

 

 

<div class="content"> <!-- 左侧SFC列表 --> <div class="sfc-list"> <div v-for="sfc in sfcList" :key="sfc.sfcNo" :class="['sfc-item', { active: activeSfc?.sfcNo === sfc.sfcNo }]" @click="selectSfc(sfc)"> <div class="sfc-header"> <span class="sfc-no" :class="statusClass(sfc.status)">{{ sfc.sfcNo }}</span> </div> <div class="sfc-meta"> <span>{{ sfc.keyInfo.length }} 工位</span> <span>{{ getSfcCompletedCount(sfc) }}/{{ getSfcTotalCount(sfc) }}</span> <el-tag v-if="getSfcCompletedCount(sfc) === 0" size="small" type="info"> 未开始 </el-tag> <el-tag v-if="getSfcCompletedCount(sfc) < getSfcTotalCount(sfc) && getSfcCompletedCount(sfc) !== 0" size="small" class="custom-light-blue"> 进行中 </el-tag> <el-tag v-if="getSfcCompletedCount(sfc) === getSfcTotalCount(sfc)" size="small" type="success"> 已完成 </el-tag> </div> </div> </div> <!-- 中间工位列表 --> <div class="station-list" v-if="activeSfc"> <div v-for="station in activeSfc.keyInfo" :key="station.station" :class="['station-item', { active: activeStation?.station === station.station }]" @click="selectStation(station)"> <div class="station-header"> <span class="station-no">{{ station.station }}</span> <el-progress :percentage="getStationProgress(station)" :stroke-width="12" :show-text="false" :color="getStationProgress(station) === 100 ? '#7fb897' : undefined" /> </div> <div class="station-meta"> <span>{{ station.keys.length }} 物料</span> <el-tag size="small"> {{ getCompletedCount(station) }}/{{ station.keys.length }} 完成 </el-tag> </div> </div> </div> <!-- 右侧工艺流 --> <div class="process-flow" v-if="activeMaterial"> <div class="process-header"> <h3>关键零部件检测</h3> </div> <div class="vertical-process"> <!-- 纵向步骤条 --> <el-steps direction="vertical" :active="currentStep" finish-status="success"> <!-- 第一步:拍照 --> <el-step title="拍照" > <template #description> <div v-if="currentStep === 1" class="step-content"> <div class="step-section"> <div class="camera-area"> <div class="camera-preview"> <img v-if="activeMaterial.imageUrl" :src="activeMaterial.imageUrl" alt="物料照片" class="preview-image" /> <div v-else class="camera-placeholder"> <el-icon :size="60"><Camera /></el-icon> <p>点击下方按钮进行拍照</p> </div> </div> <div class="camera-controls"> <!-- 打开摄像头按钮 --> <el-button type="primary" @click="openCamera"> <el-icon><VideoCamera /></el-icon> 打开摄像头 </el-button> <input ref="fileInput" type="file" accept="image/*" @change="uploadImage" style="position:absolute;visibility:hidden"> <el-button @click="fileInput?.click()" > <el-icon><Upload /></el-icon> 上传图片 </el-button> <!-- <el-button @click="uploadImage"> <el-icon><Upload /></el-icon> 上传图片 </el-button> --> <el-button type="primary" @click="nextStep" :disabled="!activeMaterial.imageUrl"> 下一步 </el-button> </div> </div> </div> </div> <!-- 摄像头浮层 --> <div v-if="cameraVisible" class="camera-overlay"> <div class="camera-container"> <div class="camera-header"> <h3>拍照</h3> <el-button icon="Close" circle @click="closeCamera" /> </div> <div class="camera-view"> <video ref="videoRef" autoplay playsinline class="camera-feed"></video> <canvas ref="canvasRef" class="camera-canvas" style="display: none;"></canvas> </div> <div class="camera-actions"> <el-button type="primary" @click="capturePhoto"> <el-icon><Camera /></el-icon> 拍照 </el-button> <el-button @click="retakePhoto"> <el-icon><Refresh /></el-icon> 重拍 </el-button> </div> </div> </div> </template> </el-step> <!-- 第二步:检测 --> <el-step title="检测" > <template #description> <div v-if="currentStep === 2" class="step-content"> <div class="step-section"> <div class="ocr-detection"> <div class="ocr-controls"> <el-form label-width="80px"> <el-form-item label="类别"> <el-select v-model="selectedTypeValue" placeholder="选择类别"> <el-option v-for="item in ocrSettings.category" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="工位"> <el-input v-model="ocrSettings.station" placeholder="当前工位" disabled /> </el-form-item> <el-form-item label="二维码" class="custom-input"> <el-switch v-model="selectedQRValue" active-text="开启" inactive-text="关闭" /> </el-form-item> <el-form-item label="阈值"> <el-slider v-model="ocrSettings.threshold" :min="0" :max="100" :step="5" show-input /> </el-form-item> <el-form-item> <el-button type="primary" :icon="ocrSettings.active ? SwitchButton : Open" @click="toggleOcr" > {{ ocrSettings.active ? '关闭' : '启动' }} OCR </el-button> </el-form-item> <el-form-item> <el-button type="success" :icon="Promotion" @click="detection" :disabled="!ocrSettings.active" > 开始识别 </el-button> </el-form-item> </el-form> </div> <div class="ocr-preview"> <div class="image-container"> <img :src="activeMaterial.imageUrl" alt="待识别图片" class="ocr-image" /> <!-- <div v-if="ocrResult" class="ocr-result-overlay"> <div v-for="(box, idx) in ocrResult.boxes" :key="idx" class="ocr-box" :style="{ left: `${box.x}%`, top: `${box.y}%`, width: `${box.width}%`, height: `${box.height}%` }" > <span class="ocr-label">{{ box.label }}</span> </div> </div> --> </div> <div class="ocr-actions"> <el-button type="primary" @click="prevStep">上一步</el-button> <el-button type="primary" @click="nextStep" :disabled="ocrResult.uuid == ''"> 下一步 </el-button> </div> </div> </div> </div> </div> </template> </el-step> <!-- 第三步:校验 --> <el-step title="校验" > <template #description> <div v-if="currentStep === 3" class="step-content"> <div class="step-section"> <div class="verification-area"> <div class="verification-header"> <!-- <h4>结果校验</h4> --> <el-tag type="warning">请确认以下信息</el-tag> </div> <div class="form-image-container"> <el-form label-width="80px"> <!-- <el-form-item label="物料号"> <el-input v-model="ocrResult.materialNo" placeholder="物料号" /> <div class="ocr-source"> <el-tag v-if="ocrResult.materialNo !== undefined" size="small" :type="ocrResult.materialNo ? 'success' : 'error'"> OCR-物料号: {{ ocrResult.materialNo || '空'}} </el-tag> </div> </el-form-item> <el-form-item label="生产商"> <el-input v-model="ocrResult.supplierNo" placeholder="生产商" /> <div class="ocr-source"> <el-tag v-if="ocrResult.supplierNo !== undefined" size="small" :type="ocrResult.supplierNo ? 'success' : 'error'"> OCR-生产商: {{ ocrResult.supplierNo || '空'}} </el-tag> </div> </el-form-item> --> <el-form-item label="物料号"> <el-select v-model="ocrResult.materialNo" placeholder="请选择物料号" :class="{'success-border': isMaterialValid, 'error-border': !isMaterialValid}"> <el-option v-for="part in remainingPartsVaild" :key="part" :label="part" :value="part"> </el-option> </el-select> <div class="ocr-source"> <el-tag v-if="ocrResult.materialNo !== undefined" size="small" :type="isMaterialValid(activeStation!) ? 'success' : 'error'"> OCR-物料号: {{ ocrResult.materialNo || '空' }} </el-tag> </div> </el-form-item> <el-form-item label="生产商"> <el-select v-model="ocrResult.supplierNo" placeholder="请选择生产商" :class="{'success-border': isSupplierValid, 'error-border': !isSupplierValid}"> <el-option v-for="supplier in supplierNosVaild" :key="supplier" :label="supplier" :value="supplier"> </el-option> </el-select> <div class="ocr-source"> <el-tag v-if="ocrResult.supplierNo !== undefined" size="small" :type="isSupplierValid(activeStation!) ? 'success' : 'error'"> OCR-生产商: {{ ocrResult.supplierNo || '空' }} </el-tag> </div> </el-form-item> <el-form-item label="序列号"> <el-input v-model="ocrResult.serialNo" placeholder="序列号" /> <div class="ocr-source"> <el-tag v-if="ocrResult.serialNo !== undefined" size="small" :type="ocrResult.serialNo ? 'success' : 'error'"> OCR-序列号: {{ ocrResult.serialNo || '空'}} </el-tag> </div> </el-form-item> </el-form> <div class="image-container"> <img :src="activeMaterial.imageUrl" alt="待识别图片" class="ocr-image" /> </div> </div> <div class="step-actions"> <el-button @click="prevStep">上一步</el-button> <el-button type="primary" @click="nextStep" :disabled="shouldDisableButton()" > 下一步 </el-button> </div> </div> </div> </div> </template> </el-step> <!-- 第四步:完成 --> <el-step title="完成" > <template #description> <div v-if="currentStep === 4" class="step-content"> <div class="step-section"> <div class="completion-area" > <el-result icon="success" title="流程完成"> <template #extra> <div class="summary-info"> <el-descriptions :column="1" border> <el-descriptions-item label="SFC"> {{ activeSfc?.sfcNo }} </el-descriptions-item> <el-descriptions-item label="工位"> {{ activeStation?.station }} </el-descriptions-item> <el-descriptions-item label="生产商"> <div style=" display: flex; justify-content: space-between; align-items: center; width: 100%"> <span style=" flex: 1; overflow: hidden; text-overflow: ellipsis;white-space: nowrap;padding-right: 10px"> {{ ocrResult?.supplierNo }} </span> <div style=" display: flex; gap: 0px; flex-shrink: 0"> <el-button type="primary" size="small" @click="handleQRcode(ocrResult?.supplierNo)" > 二维码 </el-button> <el-button type="primary" size="small" @click="handleBarcode(ocrResult?.supplierNo)" > 条形码 </el-button> </div> </div> </el-descriptions-item> <el-descriptions-item label="物料号"> <div style=" display: flex; justify-content: space-between; align-items: center; width: 100%"> <span style=" flex: 1; overflow: hidden; text-overflow: ellipsis;white-space: nowrap;padding-right: 10px"> {{ ocrResult?.materialNo }} </span> <div style=" display: flex; gap: 0px; flex-shrink: 0"> <el-button type="primary" size="small" @click="handleQRcode(ocrResult?.materialNo)" > 二维码 </el-button> <el-button type="primary" size="small" @click="handleBarcode(ocrResult?.materialNo)" > 条形码 </el-button> </div> </div> </el-descriptions-item> <el-descriptions-item label="序列号"> <div style=" display: flex; justify-content: space-between; align-items: center; width: 100%"> <span style=" flex: 1; overflow: hidden; text-overflow: ellipsis;white-space: nowrap;padding-right: 10px"> {{ ocrResult?.serialNo }} </span> <div style=" display: flex; gap: 0px; flex-shrink: 0"> <el-button type="primary" size="small" @click="handleQRcode(ocrResult?.serialNo)" > 二维码 </el-button> <el-button type="primary" size="small" @click="handleBarcode(ocrResult?.serialNo)" > 条形码 </el-button> </div> </div> </el-descriptions-item> <el-descriptions-item label="完成时间"> {{ dayjs(new Date()).format('YYYY-MM-DD hh:mm:ss') }} </el-descriptions-item> </el-descriptions> <!-- <div class="verification-summary"> <h4>验证信息</h4> <p>供应商号: {{ ocrResult.supplierNo }}</p> <p>物料号: {{ ocrResult.materialNo }}</p> <p>序列号: {{ ocrResult.serialNo }}</p> </div> --> <div v-if="showQR" class="modal-mask" @click.self="closeModal"> <div class="modal-container"> <qrcode-vue :value="qrText" :size="200" level="H" class="qr-code" /> <button class="close-btn" @click="closeModal">×</button> </div> </div> <div> <BarcodeGenerator ref="barcodeRef" />现在希望右侧工艺流是点击左侧中间工位后弹跳的新的页面上显示并操作 </div> </div> </template> </el-result> </div> <div class="step-actions"> <el-button @click="prevStep">上一步</el-button> <el-button type="success" @click="completeProcess">确认提交</el-button> </div> </div> </div> </template> </el-step> </el-steps> </div> </div> <div v-else-if="activeStation" class="no-material"> <el-empty description="该工位没有需要处理的物料" /> </div> <div v-else-if="activeSfc" class="no-station"> <el-empty description="请选择工位开始处理" /> </div> <div v-else class="no-sfc"> <el-empty description="请选择SFC开始处理" /> </div> </div>
最新发布
09-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值