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、效果图:
生成QR code
解码QR code
3、使用d-project生成QRcdoe
1)将com.d_project.qrcode.jar引入工程
2)QRcodeAction代码:
public void generate(RequestContext rc) 002 throws UnsupportedEncodingException, IOException, ServletException { 003 //待转数据 004 String data = rc.param("data", "http://osctools.net/qr"); 005 //输出图片类型 006 String output = rc.param("output", "image/jpeg"); 007 008 int type = rc.param("type", 4); 009 if (type < 0 || 10 < type) { 010 return; 011 } 012 013 int margin = rc.param("margin", 10); 014 if (margin < 0 || 32 < margin) { 015 return; 016 } 017 018 int cellSize = rc.param("size", 4); 019 if (cellSize < 1 || 4 < cellSize) { 020 return; 021 } 022 023 int errorCorrectLevel = 0; 024 025 try { 026 errorCorrectLevel = parseErrorCorrectLevel(rc, 027 rc.param("error", "H")); 028 } catch (Exception e) { 029 return; 030 } 031 032 com.d_project.qrcode.QRCode qrcode = null; 033 try { 034 qrcode = getQRCode(data, type, errorCorrectLevel); 035 } catch (Exception e) { 036 return; 037 } 038 039 if ("image/jpeg".equals(output)) { 040 041 BufferedImage image = qrcode.createImage(cellSize, margin); 042 043 rc.response().setContentType("image/jpeg"); 044 045 OutputStream out = new BufferedOutputStream(rc.response() 046 .getOutputStream()); 047 048 try { 049 ImageIO.write(image, "jpeg", out); 050 } finally { 051 out.close(); 052 } 053 054 } else if ("image/png".equals(output)) { 055 056 BufferedImage image = qrcode.createImage(cellSize, margin); 057 058 rc.response().setContentType("image/png"); 059 060 OutputStream out = new BufferedOutputStream(rc.response() 061 .getOutputStream()); 062 063 try { 064 ImageIO.write(image, "png", out); 065 } finally { 066 out.close(); 067 } 068 069 } else if ("image/gif".equals(output)) { 070 071 GIFImage image = createGIFImage(qrcode, cellSize, margin); 072 073 rc.response().setContentType("image/gif"); 074 075 OutputStream out = new BufferedOutputStream(rc.response() 076 .getOutputStream()); 077 078 try { 079 image.write(out); 080 } finally { 081 out.close(); 082 } 083 084 } else { 085 return; 086 } 087 088 } 089 090 private static int parseErrorCorrectLevel(RequestContext rc, String ecl) { 091 if ("L".equals(ecl)) { 092 return ErrorCorrectLevel.L; 093 } else if ("Q".equals(ecl)) { 094 return ErrorCorrectLevel.Q; 095 } else if ("M".equals(ecl)) { 096 return ErrorCorrectLevel.M; 097 } else if ("H".equals(ecl)) { 098 return ErrorCorrectLevel.H; 099 } else { 100 throw rc.error("qr_error_correct_error"); 101 } 102 103 } 104 105 106 private static QRCode getQRCode(String text, int typeNumber, 107 int errorCorrectLevel) throws IllegalArgumentException { 108 if (typeNumber == 0) { 109 return QRCode.getMinimumQRCode(text, errorCorrectLevel); 110 } else { 111 QRCode qr = new QRCode(); 112 qr.setTypeNumber(typeNumber); 113 qr.setErrorCorrectLevel(errorCorrectLevel); 114 qr.addData(text); 115 qr.make(); 116 return qr; 117 } 118 } 119 120 private static GIFImage createGIFImage(QRCode qrcode, int cellSize, 121 int margin) throws IOException { 122 123 int imageSize = qrcode.getModuleCount() * cellSize + margin * 2; 124 125 GIFImage image = new GIFImage(imageSize, imageSize); 126 127 for (int y = 0; y < imageSize; y++) { 128 129 for (int x = 0; x < imageSize; x++) { 130 131 if (margin <= x && x < imageSize - margin && margin <= y 132 && y < imageSize - margin) { 133 134 int col = (x - margin) / cellSize; 135 int row = (y - margin) / cellSize; 136 137 if (qrcode.isDark(row, col)) { 138 image.setPixel(x, y, 0); 139 } else { 140 image.setPixel(x, y, 1); 141 } 142 143 } else { 144 image.setPixel(x, y, 1); 145 } 146 } 147 } 148 149 return image; 150 }3)前端页面:
<script type="text/javascript" src="/js/jquery/jquery.form-2.82.js"></script> 002 003 <script type="text/javascript"> 004 005 $(document).ready(function(){ 006 007 $("#submit").click(function(){ 008 009 var url = "/action/qrcode/generate?" + $("#qrcode_form").serialize(); 010 011 $(".QRCodeDiv img").attr("src",url+"&"+new Date().getTime()); 012 013 $("#gen_url").attr("href",url); 014 015 }); 016 017 $("#zxing").popover({ 018 019 'title':'条形码处理类库 ZXing', 020 021 'content':'ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。', 022 023 'placement':'bottom' 024 025 }); 026 027 }); 028 029 </script> 030 031 <div id="mainContent" class="wrapper"> 032 033 <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> 034 035 <div class="toolUsing clearfix"> 036 037 <div class="toolsTab clearfix"> 038 039 <ul class="nav nav-tabs"> 040 041 <li class="active"><a href="/qr">转QR码</a></li> 042 043 <li ><a href="/qr?type=2">二维码解码</a></li> 044 045 </ul> 046 047 <div class="clear"></div> 048 049 </div> 050 051 <form id="qrcode_form" method="post" > 052 053 <div class="leftBar"> 054 055 <div class="title">URL或其他文本:</div> 056 057 <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> 058 059 </div> 060 061 <div class="operateLR"> 062 063 <div class="OptDetail span1"> 064 065 <label>输出格式:</label> 066 067 <select name="output" class="span1"> 068 069 <option value="image/gif" selected>GIF</option> 070 071 <option value="image/jpeg">JPEG</option> 072 073 <option value="image/png">PNG</option> 074 075 </select> 076 077 <label>纠错级别:</label> 078 079 <select name="error" class="span1"> 080 081 <option value="L" selected>L 7%</option> 082 083 <option value="M">M 15%</option> 084 085 <option value="Q">Q 25%</option> 086 087 <option value="H">H 30%</option> 088 089 </select> 090 091 <label>类型:</label> 092 093 <select name="type" class="span1"> 094 095 <option value="0">自动</option> 096 097 <option value="1">1</option> 098 099 <option value="2">2</option> 100 101 <option value="3">3</option> 102 103 <option value="4">4</option> 104 105 <option value="5">5</option> 106 107 <option value="6">6</option> 108 109 <option value="7">7</option> 110 111 <option value="8">8</option> 112 113 <option value="9">9</option> 114 115 <option value="10">10</option> 116 117 </select> 118 119 <label>边缘留白:</label> 120 121 <select name="margin" class="span1"> 122 123 <option value="0">0</option> 124 125 <option value="1">1</option> 126 127 <option value="2">2</option> 128 129 <option value="3">3</option> 130 131 <option value="4">4</option> 132 133 <option value="5">5</option> 134 135 <option value="6">6</option> 136 137 <option value="7">7</option> 138 139 <option value="8">8</option> 140 141 <option value="9">9</option> 142 143 <option value="10">10</option> 144 145 <option value="11">11</option> 146 147 <option value="12">12</option> 148 149 <option value="13">13</option> 150 151 <option value="14">14</option> 152 153 <option value="15">15</option> 154 155 <option value="16">16</option> 156 157 <option value="17">17</option> 158 159 <option value="18">18</option> 160 161 <option value="19">19</option> 162 163 <option value="20">20</option> 164 165 <option value="21">21</option> 166 167 <option value="22">22</option> 168 169 <option value="23">23</option> 170 171 <option value="24">24</option> 172 173 <option value="25">25</option> 174 175 <option value="26">26</option> 176 177 <option value="27">27</option> 178 179 <option value="28">28</option> 180 181 <option value="29">29</option> 182 183 <option value="30">30</option> 184 185 <option value="31">31</option> 186 187 <option value="32">32</option> 188 189 </select> 190 191 <label>原胞大小:</label> 192 193 <select name="size" class="span1"> 194 195 <option value="1" >1</option> 196 197 <option value="2" >2</option> 198 199 <option value="3" >3</option> 200 201 <option value="4" selected >4</option> 202 203 </select> 204 205 <button class="btn btn-small btn-primary" id="submit" onclick="return false;">生成QR码</button> </div> 206 207 </div> 208 209 <div class="rightBar"> 210 211 <div class="title">QR码:</div> 212 213 <div class="QRCodeDiv"> 214 215 <div class="QRWrapper"> 216 217 <a id="gen_url" href="/action/qrcode/generate?size=4" target="_blank"><img src="/action/qrcode/generate?size=4"/></a> 218 219 </div> 220 221 </div> 222 223 </div> 224 225 </form> 226 227 </div> 228 229 </div>
4、使用ZXing解码QRcode
1)下载Zxing-2.0.zip
2)引入zxing-barcode_core.jar与zxing_barcode_j2se.jar到工程
3)QRcodeAction代码:
@PostMethod 002 @JSONOutputEnabled 003 public void decode(RequestContext rc) throws IOException { 004 //存在qrcode的网址 005 String url = rc.param("url", ""); 006 //待解码的qrcdoe图像 007 File img = rc.file("qrcode"); 008 if (StringUtils.isBlank(url) && img == null) { 009 throw rc.error("qr_upload_or_url_null"); 010 } 011 012 List<Result> results = new ArrayList<Result>(); 013 Config config = new Config(); 014 Inputs inputs = new Inputs(); 015 016 config.setHints(buildHints(config)); 017 018 if (StringUtils.isNotBlank(url)) { 019 addArgumentToInputs(url, config, inputs); 020 } 021 if (img != null) { 022 inputs.addInput(img.getCanonicalPath()); 023 } 024 while (true) { 025 String input = inputs.getNextInput(); 026 if (input == null) { 027 break; 028 } 029 File inputFile = new File(input); 030 if (inputFile.exists()) { 031 try { 032 Result result = decode(inputFile.toURI(), config,rc); 033 results.add(result); 034 } catch (IOException e) { 035 } 036 } else { 037 try { 038 Result result = decode(new URI(input), config,rc); 039 results.add(result); 040 } catch (Exception e) { 041 } 042 } 043 } 044 rc.print(new Gson().toJson(results)); 045 } 046 047 private Result decode(URI uri,Config config,RequestContext rc) 048 throws IOException { 049 Map<DecodeHintType, ?> hints = config.getHints(); 050 BufferedImage image; 051 try { 052 image = ImageIO.read(uri.toURL()); 053 } catch (IllegalArgumentException iae) { 054 throw rc.error("qr_resource_not_found"); 055 } 056 if (image == null) { 057 throw rc.error("qr_could_not_load_image"); 058 } 059 try { 060 LuminanceSource source = new BufferedImageLuminanceSource(image); 061 BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); 062 Result result = new MultiFormatReader().decode(bitmap, hints); 063 return result; 064 } catch (NotFoundException nfe) { 065 throw rc.error("qr_no_barcode_found"); 066 } 067 } 068 069 private static Map<DecodeHintType, ?> buildHints(Config config) { 070 Map<DecodeHintType, Object> hints = new EnumMap<DecodeHintType, Object>( 071 DecodeHintType.class); 072 Collection<BarcodeFormat> vector = new ArrayList<BarcodeFormat>(8); 073 vector.add(BarcodeFormat.UPC_A); 074 vector.add(BarcodeFormat.UPC_E); 075 vector.add(BarcodeFormat.EAN_13); 076 vector.add(BarcodeFormat.EAN_8); 077 vector.add(BarcodeFormat.RSS_14); 078 vector.add(BarcodeFormat.RSS_EXPANDED); 079 if (!config.isProductsOnly()) { 080 vector.add(BarcodeFormat.CODE_39); 081 vector.add(BarcodeFormat.CODE_93); 082 vector.add(BarcodeFormat.CODE_128); 083 vector.add(BarcodeFormat.ITF); 084 vector.add(BarcodeFormat.QR_CODE); 085 vector.add(BarcodeFormat.DATA_MATRIX); 086 vector.add(BarcodeFormat.AZTEC); 087 vector.add(BarcodeFormat.PDF_417); 088 vector.add(BarcodeFormat.CODABAR); 089 vector.add(BarcodeFormat.MAXICODE); 090 } 091 hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); 092 if (config.isTryHarder()) { 093 hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); 094 } 095 if (config.isPureBarcode()) { 096 hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE); 097 } 098 return hints; 099 } 100 101 private static void addArgumentToInputs(String argument, Config config, 102 Inputs inputs) throws IOException { 103 File inputFile = new File(argument); 104 if (inputFile.exists()) { 105 inputs.addInput(inputFile.getCanonicalPath()); 106 } else { 107 inputs.addInput(argument); 108 } 109 }
4)前端页面:
<script type="text/javascript" src="/js/jquery/jquery.form-2.82.js"></script> 002 003 <script type="text/javascript"> 004 005 $(document).ready(function(){ 006 007 $("#qrcode_form").ajaxForm({ 008 009 success:function(json){ 010 011 if(json==null) 012 013 return; 014 015 json = eval("("+json+")"); 016 017 if(json.msg){ 018 019 alert(json.msg); 020 021 return; 022 023 } 024 025 if(json[0]) 026 027 $("#result").val(json[0].text); 028 029 else 030 031 $("#result").val("解码失败"); 032 033 } 034 035 }); 036 037 $("#zxing").popover({ 038 039 'title':'条形码处理类库 ZXing', 040 041 'content':'ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。', 042 043 'placement':'bottom' 044 045 }); 046 047 }); 048 049 </script> 050 051 <div id="mainContent" class="wrapper"> 052 053 <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> 054 055 <div class="toolUsing clearfix"> 056 057 <div class="toolsTab clearfix"> 058 059 <ul class="nav nav-tabs"> 060 061 <li ><a href="/qr">转QR码</a></li> 062 063 <li class="active"><a href="/qr?type=2">二维码解码</a></li> 064 065 </ul> 066 067 <div class="clear"></div> 068 069 </div> 070 071 <form id="qrcode_form" method="post" action="/action/qrcode/decode"> 072 073 <div class="topBar"> 074 075 <div class="title"> 076 077 <label class="radio" for="upload_url">图片URL: 078 079 <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')}"/> 080 081 </label> 082 083 </div> 084 085 <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"/> 086 087 <div class="title"> 088 089 <label class="radio" for="upload_img">上传图片: 090 091 <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')}"/> 092 093 </label> 094 095 </div> 096 097 <input disabled="disabled" name="qrcode" type="file" class="input-file"/> 098 099 <input class="btn btn-primary" value="解码" type="submit"/> 100 101 </div> 102 103 <div class="bottomBar"> 104 105 <div class="title">解码结果:</div> 106 107 <textarea id="result"></textarea> 108 109 </div> 110 111 </form> 112 113 </div> 114 </div>注意:其中牵涉到的 RequestContext类见此处: http://www.oschina.net/code/snippet_12_2