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代码:
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