Java 生成 / 解码 QR码
QR码的使用越来越多,可以在很多地方见着,比如火车票、推广产品上等,以下将介绍如何用Java生成QR码以及解码QR码。
1、涉及开源项目:
ZXing :一个开源Java类库用于解析多种格式的1D/2D条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。---用来解码QRcode
d-project:Kazuhiko Arase的个人项目(他具体是谁不清楚,日本的),提供丰富的配置参数,非常灵活---用来生成QR code
2、效果图:
3、使用d-project生成QRcdoe
1)将com.d_project.qrcode.jar引入工程
2)QRcodeAction代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | public void generate(RequestContext rc) throws UnsupportedEncodingException, IOException, ServletException { //待转数据 String data = rc.param( "data" , "http://osctools.net/qr" ); //输出图片类型 String output = rc.param( "output" , "image/jpeg" ); int type = rc.param( "type" , 4 ); if (type < 0 || 10 < type) { return ; } int margin = rc.param( "margin" , 10 ); if (margin < 0 || 32 < margin) { return ; } int cellSize = rc.param( "size" , 4 ); if (cellSize < 1 || 4 < cellSize) { return ; } int errorCorrectLevel = 0 ; try { errorCorrectLevel = parseErrorCorrectLevel(rc, rc.param( "error" , "H" )); } catch (Exception e) { return ; } com.d_project.qrcode.QRCode qrcode = null ; try { qrcode = getQRCode(data, type, errorCorrectLevel); } catch (Exception e) { return ; } if ( "image/jpeg" .equals(output)) { BufferedImage image = qrcode.createImage(cellSize, margin); rc.response().setContentType( "image/jpeg" ); OutputStream out = new BufferedOutputStream(rc.response() .getOutputStream()); try { ImageIO.write(image, "jpeg" , out); } finally { out.close(); } } else if ( "image/png" .equals(output)) { BufferedImage image = qrcode.createImage(cellSize, margin); rc.response().setContentType( "image/png" ); OutputStream out = new BufferedOutputStream(rc.response() .getOutputStream()); try { ImageIO.write(image, "png" , out); } finally { out.close(); } } else if ( "image/gif" .equals(output)) { GIFImage image = createGIFImage(qrcode, cellSize, margin); rc.response().setContentType( "image/gif" ); OutputStream out = new BufferedOutputStream(rc.response() .getOutputStream()); try { image.write(out); } finally { out.close(); } } else { return ; } } private static int parseErrorCorrectLevel(RequestContext rc, String ecl) { if ( "L" .equals(ecl)) { return ErrorCorrectLevel.L; } else if ( "Q" .equals(ecl)) { return ErrorCorrectLevel.Q; } else if ( "M" .equals(ecl)) { return ErrorCorrectLevel.M; } else if ( "H" .equals(ecl)) { return ErrorCorrectLevel.H; } else { throw rc.error( "qr_error_correct_error" ); } } private static QRCode getQRCode(String text, int typeNumber, int errorCorrectLevel) throws IllegalArgumentException { if (typeNumber == 0 ) { return QRCode.getMinimumQRCode(text, errorCorrectLevel); } else { QRCode qr = new QRCode(); qr.setTypeNumber(typeNumber); qr.setErrorCorrectLevel(errorCorrectLevel); qr.addData(text); qr.make(); return qr; } } private static GIFImage createGIFImage(QRCode qrcode, int cellSize, int margin) throws IOException { int imageSize = qrcode.getModuleCount() * cellSize + margin * 2 ; GIFImage image = new GIFImage(imageSize, imageSize); for ( int y = 0 ; y < imageSize; y++) { for ( int x = 0 ; x < imageSize; x++) { if (margin <= x && x < imageSize - margin && margin <= y && y < imageSize - margin) { int col = (x - margin) / cellSize; int row = (y - margin) / cellSize; if (qrcode.isDark(row, col)) { image.setPixel(x, y, 0 ); } else { image.setPixel(x, y, 1 ); } } else { image.setPixel(x, y, 1 ); } } } return image; } |
3)前端页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | < script type="text/javascript" src="/js/jquery/jquery.form-2.82.js"></ script > < script type="text/javascript"> $(document).ready(function(){ $("#submit").click(function(){ var url = "/action/qrcode/generate?" + $("#qrcode_form").serialize(); $(".QRCodeDiv img").attr("src",url+"&"+new Date().getTime()); $("#gen_url").attr("href",url); }); $("#zxing").popover({ 'title':'条形码处理类库 ZXing', 'content':'ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。', 'placement':'bottom' }); }); </ script > < div id="mainContent" class="wrapper"> < div class="toolName">在线生成二维码(QR码)-采用< a id="zxing" href="http://www.oschina.net/p/zxing">ZXing</ a >与< a href="http://www.d-project.com/">d-project</ a >< a data-toggle="modal" href="#advice" style="float:right;text-decoration:none;">< span class="badge badge-important">< i class="icon-envelope icon-white"></ i > Feedback</ span ></ a ></ div > < div class="toolUsing clearfix"> < div class="toolsTab clearfix"> < ul class="nav nav-tabs"> < li class="active">< a href="/qr">转QR码</ a ></ li > < li >< a href="/qr?type=2">二维码解码</ a ></ li > </ ul > < div class="clear"></ div > </ div > < form id="qrcode_form" method="post" > < div class="leftBar"> < div class="title">URL或其他文本:</ div > < textarea class="input-xlarge" name="data" onfocus="if(this.value=='http://osctools.net/qr'){this.value='';};this.select();" onblur="(this.value=='')?this.value='http://osctools.net/qr':this.value;">http://osctools.net/qr</ textarea > </ div > < div class="operateLR"> < div class="OptDetail span1"> < label >输出格式:</ label > < select name="output" class="span1"> < option value="image/gif" selected>GIF</ option > < option value="image/jpeg">JPEG</ option > < option value="image/png">PNG</ option > </ select > < label >纠错级别:</ label > < select name="error" class="span1"> < option value="L" selected>L 7%</ option > < option value="M">M 15%</ option > < option value="Q">Q 25%</ option > < option value="H">H 30%</ option > </ select > < label >类型:</ label > < select name="type" class="span1"> < option value="0">自动</ option > < option value="1">1</ option > < option value="2">2</ option > < option value="3">3</ option > < option value="4">4</ option > < option value="5">5</ option > < option value="6">6</ option > < option value="7">7</ option > < option value="8">8</ option > < option value="9">9</ option > < option value="10">10</ option > </ select > < label >边缘留白:</ label > < select name="margin" class="span1"> < option value="0">0</ option > < option value="1">1</ option > < option value="2">2</ option > < option value="3">3</ option > < option value="4">4</ option > < option value="5">5</ option > < option value="6">6</ option > < option value="7">7</ option > < option value="8">8</ option > < option value="9">9</ option > < option value="10">10</ option > < option value="11">11</ option > < option value="12">12</ option > < option value="13">13</ option > < option value="14">14</ option > < option value="15">15</ option > < option value="16">16</ option > < option value="17">17</ option > < option value="18">18</ option > < option value="19">19</ option > < option value="20">20</ option > < option value="21">21</ option > < option value="22">22</ option > < option value="23">23</ option > < option value="24">24</ option > < option value="25">25</ option > < option value="26">26</ option > < option value="27">27</ option > < option value="28">28</ option > < option value="29">29</ option > < option value="30">30</ option > < option value="31">31</ option > < option value="32">32</ option > </ select > < label >原胞大小:</ label > < select name="size" class="span1"> < option value="1" >1</ option > < option value="2" >2</ option > < option value="3" >3</ option > < option value="4" selected >4</ option > </ select > < button class="btn btn-small btn-primary" id="submit" onclick="return false;">生成QR码</ button > </ div > </ div > < div class="rightBar"> < div class="title">QR码:</ div > < div class="QRCodeDiv"> < div class="QRWrapper"> < a id="gen_url" href="/action/qrcode/generate?size=4" target="_blank">< img src="/action/qrcode/generate?size=4"/></ a > </ div > </ div > </ div > </ form > </ div > </ div > |
4、使用ZXing解码QRcode
1)下载Zxing-2.0.zip
2)引入zxing-barcode_core.jar与zxing_barcode_j2se.jar到工程
3)QRcodeAction代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | @PostMethod @JSONOutputEnabled public void decode(RequestContext rc) throws IOException { //存在qrcode的网址 String url = rc.param( "url" , "" ); //待解码的qrcdoe图像 File img = rc.file( "qrcode" ); if (StringUtils.isBlank(url) && img == null ) { throw rc.error( "qr_upload_or_url_null" ); } List<Result> results = new ArrayList<Result>(); Config config = new Config(); Inputs inputs = new Inputs(); config.setHints(buildHints(config)); if (StringUtils.isNotBlank(url)) { addArgumentToInputs(url, config, inputs); } if (img != null ) { inputs.addInput(img.getCanonicalPath()); } while ( true ) { String input = inputs.getNextInput(); if (input == null ) { break ; } File inputFile = new File(input); if (inputFile.exists()) { try { Result result = decode(inputFile.toURI(), config,rc); results.add(result); } catch (IOException e) { } } else { try { Result result = decode( new URI(input), config,rc); results.add(result); } catch (Exception e) { } } } rc.print( new Gson().toJson(results)); } private Result decode(URI uri,Config config,RequestContext rc) throws IOException { Map<DecodeHintType, ?> hints = config.getHints(); BufferedImage image; try { image = ImageIO.read(uri.toURL()); } catch (IllegalArgumentException iae) { throw rc.error( "qr_resource_not_found" ); } if (image == null ) { throw rc.error( "qr_could_not_load_image" ); } try { LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap( new HybridBinarizer(source)); Result result = new MultiFormatReader().decode(bitmap, hints); return result; } catch (NotFoundException nfe) { throw rc.error( "qr_no_barcode_found" ); } } private static Map<DecodeHintType, ?> buildHints(Config config) { Map<DecodeHintType, Object> hints = new EnumMap<DecodeHintType, Object>( DecodeHintType. class ); Collection<BarcodeFormat> vector = new ArrayList<BarcodeFormat>( 8 ); vector.add(BarcodeFormat.UPC_A); vector.add(BarcodeFormat.UPC_E); vector.add(BarcodeFormat.EAN_13); vector.add(BarcodeFormat.EAN_8); vector.add(BarcodeFormat.RSS_14); vector.add(BarcodeFormat.RSS_EXPANDED); if (!config.isProductsOnly()) { vector.add(BarcodeFormat.CODE_39); vector.add(BarcodeFormat.CODE_93); vector.add(BarcodeFormat.CODE_128); vector.add(BarcodeFormat.ITF); vector.add(BarcodeFormat.QR_CODE); vector.add(BarcodeFormat.DATA_MATRIX); vector.add(BarcodeFormat.AZTEC); vector.add(BarcodeFormat.PDF_417); vector.add(BarcodeFormat.CODABAR); vector.add(BarcodeFormat.MAXICODE); } hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); if (config.isTryHarder()) { hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); } if (config.isPureBarcode()) { hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE); } return hints; } private static void addArgumentToInputs(String argument, Config config, Inputs inputs) throws IOException { File inputFile = new File(argument); if (inputFile.exists()) { inputs.addInput(inputFile.getCanonicalPath()); } else { inputs.addInput(argument); } } |
4)前端页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | < script type="text/javascript" src="/js/jquery/jquery.form-2.82.js"></ script > < script type="text/javascript"> $(document).ready(function(){ $("#qrcode_form").ajaxForm({ success:function(json){ if(json==null) return; json = eval("("+json+")"); if(json.msg){ alert(json.msg); return; } if(json[0]) $("#result").val(json[0].text); else $("#result").val("解码失败"); } }); $("#zxing").popover({ 'title':'条形码处理类库 ZXing', 'content':'ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。', 'placement':'bottom' }); }); </ script > < div id="mainContent" class="wrapper"> < div class="toolName">在线生成二维码(QR码)-采用< a id="zxing" href="http://www.oschina.net/p/zxing">ZXing</ a >与< a href="http://www.d-project.com/">d-project</ a >< a data-toggle="modal" href="#advice" style="float:right;text-decoration:none;">< span class="badge badge-important">< i class="icon-envelope icon-white"></ i > Feedback</ span ></ a ></ div > < div class="toolUsing clearfix"> < div class="toolsTab clearfix"> < ul class="nav nav-tabs"> < li >< a href="/qr">转QR码</ a ></ li > < li class="active">< a href="/qr?type=2">二维码解码</ a ></ li > </ ul > < div class="clear"></ div > </ div > < form id="qrcode_form" method="post" action="/action/qrcode/decode"> < div class="topBar"> < div class="title"> < label class="radio" for="upload_url">图片URL: < input checked="checked" name="upload_ctn" id="upload_url" style="margin-right:5px;" type="radio" onchange="if(this.checked){$('input[name=\'url\']').removeAttr('disabled');$('input[name=\'qrcode\']').attr('disabled','disabled')}"/> </ label > </ div > < input name="url" id="url" style="width:100%;height:40px;margin:0 0 10px 0;" onfocus="if(this.value=='http://www.osctools.net/img/qr.gif'){this.value='';};this.select();" onblur="(this.value=='')?this.value='http://www.osctools.net/img/qr.gif':this.value;" value="http://www.osctools.net/img/qr.gif"/> < div class="title"> < label class="radio" for="upload_img">上传图片: < input style="margin-right:5px;" name="upload_ctn" id="upload_img" type="radio" onchange="if(this.checked){$('input[name=\'qrcode\']').removeAttr('disabled');$('input[name=\'url\']').attr('disabled','disabled')}"/> </ label > </ div > < input disabled="disabled" name="qrcode" type="file" class="input-file"/> < input class="btn btn-primary" value="解码" type="submit"/> </ div > < div class="bottomBar"> < div class="title">解码结果:</ div > < textarea id="result"></ textarea > </ div > </ form > </ div > </ div > |
注意:其中牵涉到的RequestContext类见此处:http://www.oschina.net/code/snippet_12_2
分类:
Java学习之路......
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通