講解一篇Java代碼根據(jù)參數(shù)動(dòng)態(tài)生成PC二維碼效果,且成功掃描并上傳圖形或視頻資源的功能。
技術(shù)難度一般,關(guān)鍵在于如何把一整套邏輯思路整合到項(xiàng)目上,如果調(diào)用,應(yīng)該到哪些技術(shù),理清了交互關(guān)系,詳細(xì)對(duì)于大家而言這就是一份入門(mén)級(jí)別的代碼參考,以作提升。
粗略介紹一下應(yīng)用到的技術(shù)問(wèn)題,前端方法使用簡(jiǎn)單的html元素布局,生成<img>二維碼即可,后端框架為SpringMVC,結(jié)構(gòu)簡(jiǎn)單,查閱清晰,應(yīng)用到的二維碼Jar包為:qrcode_swetake.jar 。
一、從前端開(kāi)始入手,先構(gòu)建頁(yè)面布局確保能夠生成二維碼效果:
實(shí)現(xiàn)出來(lái)的成型效果如上圖所示,鼠標(biāo)移入移出則顯示隱藏二維碼效果框,自行慢慢摸索CSS如何實(shí)現(xiàn)了,這里需要說(shuō)明的是二維碼只需要一句話(huà)即可動(dòng)態(tài)生成。
<c:set var="ctx" value="${pageContext.request.contextPath}"></c:set> <img src="${ctx}/upload/codeImg.html?userId=${loginUserId}" />
設(shè)置一個(gè)<img>標(biāo)簽動(dòng)態(tài)生成接收返回的二維碼圖,高寬度自行根據(jù)實(shí)際情況設(shè)定,然后就嗶的一聲手機(jī)即可訪(fǎng)問(wèn)的,當(dāng)然測(cè)試階段請(qǐng)確保IP同在一個(gè)局域網(wǎng)。
掃描出來(lái)的頁(yè)面也異曲同工,關(guān)鍵在于你要設(shè)計(jì)成什么樣子,只要能實(shí)現(xiàn)上傳功能即可。
我應(yīng)用的前端上傳插件為jquery.form.js,主要JS方法如下:
<input type="file" name="tempFile" id="tempFile" accept= ".avi,.mpg,.mp4,.mov;capture=camera" onchange="previewFile('tempFile');"/> <input type="file" name="tempFile" id="tempFile" accept= "image/jpg,image/JPG,image/jpeg,image/JPEG,image/pdf,image/png;capture=camera" onchange="previewFile('tempFile');"/> <script language="javascript"> function previewFile(id){ var x = document.getElementById("tempFile"); if(!x || !x.value) return; var patn = /.jpg$|.JPG$|.jpeg$|.JPEG$|.pdf$|.PDF$|.png$|.PNG$/; var inpType = "image/jpg,image/JPG,image/jpeg,image/JPEG,image/pdf,image/PDF,image/png,image/PNG"; var type = $("#type").val(); if(type=='video'){ patn = /.mp4$|.MP4$|.mov$|.MOV$/; inpType = ".MP4,.MOV,.mp4,.mov"; } if(!patn.test(x.value)){ if(type=='video'){ alertMsg("請(qǐng)上傳mov、mp4格式的視頻"); }else{ alertMsg("請(qǐng)上傳jpg、png、pdf格式的文件"); } }else{ initShow(); $("#uploadForm").ajaxSubmit(options); } } var options = { dataType:"json", url:'${ctx}/upload/uploadAndSaveFile.html', type:'post', contentType: "application/x-www-form-urlencoded; charset=utf-8", success:function(data) { initHide(); if(data.success){ //alertCallBack("上傳成功<br/>請(qǐng)前往PC端刷新后查閱!",function(){ document.location.href="${ctx}/upload/toUploadEndDetail.html"; //}); }else{ alertCallBack(data.message,function(){ window.location.reload(); }); //alertMsg(data.message); } }, error :function(data){ initHide(); alertMsg(data); }, beforeSubmit:showRequest // pre-submit callback //clearForm:true }; function showRequest(formData, jqForm, options) { var queryString = $.param(formData); } </script>
以上代碼由于涉及公司內(nèi)部邏輯,故存在個(gè)別地方的刪減動(dòng)作,但整體思路已清晰羅列出來(lái)了。
這里需要講到一個(gè)應(yīng)用到的UI插件,即alertMsg使用到的hint彈出框效果。smallarAlert.js
附件資源均會(huì)統(tǒng)一部署上來(lái),插件自適應(yīng)效果可兼容IE、谷歌、360、火狐等主流瀏覽器,內(nèi)置樣式及功能等也可自行重寫(xiě),具體應(yīng)用自行度娘搜索該插件了,這里就不一一講解了。
二、從后端著手處理二維碼生成技術(shù)及資源保存動(dòng)作:
/** * @Descript :生成二維碼圖片url * @author : Teny_lau * @CreatedTime : 2016年11月21日-下午3:44:44 * @param model * @param request * @param response */ @RequestMapping("/codeImg") public void toCodeImg(Model model, HttpServletRequest request, HttpServletResponse response) { String localIp = getInternetIp(request); String path = request.getSession().getServletContext().getContextPath(); String port = StringUtils.getNullBlankStr(request.getServerPort());// 獲取服務(wù)器端口 String userId = StringUtils.getNullBlankStr(request.getParameter("userId"));//接收參數(shù) String params = "userId=" + userId; // 字節(jié)長(zhǎng)度須控制在124個(gè)長(zhǎng)度以?xún)?nèi),否則報(bào)異常數(shù)組索引值溢出 String content = localIp + ":" + port + path + "/upload/toUploadMain.html?" + params; EncoderHandler encoder = new EncoderHandler(); encoder.encoderQRCoder(content, response); } private String getInternetIp(HttpServletRequest request) { String ip = StringUtils.getNullBlankStr(request.getHeader("x-forwarded-for")); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = StringUtils.getNullBlankStr(request.getHeader("Proxy-Client-IP")); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = StringUtils.getNullBlankStr(request.getHeader("WL-Proxy-Client-IP")); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = StringUtils.getNullBlankStr(request.getRemoteAddr());// 獲取客戶(hù)端IP地址 } String localIp = "";// 獲取網(wǎng)段地址 try { InetAddress localInter = InetAddress.getLocalHost(); localIp = StringUtils.getNullBlankStr(localInter.getHostAddress()); } catch (UnknownHostException e1) { e1.printStackTrace(); } if (!ip.equals(localIp) && !"127.0.0.1".equals(ip)) { // 當(dāng)程序獲取非服務(wù)器網(wǎng)口時(shí),自動(dòng)重置為本地網(wǎng)口 ip = localIp; } if ("127.0.0.1".equals(ip)) { // 根據(jù)網(wǎng)卡取本機(jī)配置的IP InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch (Exception e) { e.printStackTrace(); } ip = StringUtils.getNullBlankStr(inet.getHostAddress()); } // 對(duì)于通過(guò)多個(gè)代理的情況,第一個(gè)IP為客戶(hù)端真實(shí)IP,多個(gè)IP按照','分割 if (ip != null && ip.length() > 15) { // "***.***.***.***".length() = 15 if (ip.indexOf(",") > 0) { ip = ip.substring(0, ip.indexOf(",")); } } return ip; } /** * 提交資源信息 * * @param model * @param request * @return */ @RequestMapping("uploadAndSaveFile") @ResponseBody public void uploadAndSaveFile(HttpServletRequest request, HttpServletResponse response) { Map<String, Object> result = new HashMap<String, Object>(); try { String type = StringUtils.getNullBlankStr(request.getParameter("type")); saveUploadFile(request); result.put("success", true); } catch (RuntimeException run) { result.put("success", false); result.put("message", run.getMessage()); } catch (Exception e) { result.put("success", false); result.put("message", "上傳失敗<br/>請(qǐng)重新或掃碼上傳!"); // FileUtil.deleteFiles(fileNames); } try { response.setContentType("text/html;charset=gbk"); JSONObject jsonObj = JSONObject.fromObject(result); response.getWriter().print(jsonObj); } catch (IOException e) { e.printStackTrace(); } } /** * @Descript :保存上傳資源 * @author : Teny_lau * @CreatedTime : 2016年11月21日-下午3:28:39 */ private void saveUploadFile(HttpServletRequest request) { String userId = StringUtils.getNullBlankStr(request.getParameter("userId"));//接收參數(shù) Map<String, Object> queryMap = new HashMap<String, Object>(); queryMap.put("",""); List<XXX> flashList = new ArrayList<XXX>(); String oldFlashName = "";// 舊文件 //由于以下邏輯存在項(xiàng)目代碼,故以下對(duì)象均為舉例說(shuō)明,具體要求視項(xiàng)目自行修改 Old oldEntity = new Old(); if (flashList != null && flashList.size() > 0) { oldEntity = flashList.get(0); //獲取已上傳的舊文件名,便于在插入新文件時(shí),刪除舊文件,避免資源過(guò)多占用空間內(nèi)存 oldFlashName = oldEntity.getFlashName(); } MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; CommonsMultipartFile file1 = (CommonsMultipartFile) multipartRequest.getFile("tempFile");// 上傳資源參數(shù) if (file1 != null && !file1.isEmpty()) { // 判斷上傳資源等文件大小 控制在500M以?xún)?nèi),自行根據(jù)項(xiàng)目要求斟酌 int maxSize = 500 * 1024 * 1024; if (file1.getSize() >= maxSize) { throw new RuntimeException("保存失敗,文件控制在500M以?xún)?nèi)"); } String fileName = saveFile(file1, request, oldFlashName);//這里返回的為重命名新文件名稱(chēng) } } private String saveFile(CommonsMultipartFile file, HttpServletRequest request, String oldFileName) { String fileName = file.getOriginalFilename(); String uploadPath = request.getSession().getServletContext().getRealPath(FILEPATH); // 判斷是否有上傳文件 File targetFile = null; String groupId = StringUtils.getNullBlankStr(request.getParameter("groupId"));//接收參數(shù) String newTransFileName = DateUtils.getCurrentDateTime14() + groupId + "." + org.apache.commons.lang3.StringUtils.substringAfterLast(fileName, "."); String newFilePath = uploadPath + File.separator + newTransFileName; try { targetFile = new File(new StringBuilder().append(newFilePath).toString()); // 文件重命名 file.transferTo(targetFile); String oldFilePath = ""; if (StringUtils.isNotBlank(oldFileName)) { oldFilePath = uploadPath + File.separator + oldFileName; FileUtil.delSingleFile(oldFilePath); } // 復(fù)制文件到指定盤(pán) // CopyFileUtil.copyFile(origiNewPath, oldFilePath, true); } catch (NullPointerException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return newTransFileName; }
代碼先一一貼上,以上代碼由于存在公司邏輯,故也做了個(gè)別刪減動(dòng)作,但整體思路的增刪改上傳等功能已一一展示在這里了。
個(gè)別工具類(lèi)方法如下:
1、StringUtils.getNullBlankStr
/** * 功能描述: * 判斷字符串是否為null或?yàn)榭兆址?,則返回空字符串"" * * @param obj String * 待檢查的字符串 * @return String * 如果為null或空字符串(包括只含空格的字符串)則返回"",否則返回原字符串去空格 */ public static String getNullBlankStr(Object obj) { if (obj == null) { return ""; } else { return obj.toString().trim(); } }
校驗(yàn)判斷為空時(shí)則返回空字符串的動(dòng)作。
2、DateUtils.getCurrentDateTime14()
/** * 獲取當(dāng)前應(yīng)用服務(wù)器的系統(tǒng)日期時(shí)間 * * @return 日期時(shí)間字符串,格式:yyyyMMddHHmmss */ public static String getCurrentDateTime14() { DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); return df.format(new Date(System.currentTimeMillis())); }
具體時(shí)間格式可自行定義,根據(jù)具體情況而定。
3、EncoderHandler和FileUtil 工具類(lèi),參考附件文件源碼。
特別注意:
這里的EncoderHandler需要提醒到的一個(gè)問(wèn)題點(diǎn)是,該方法接收字節(jié)長(zhǎng)度是有最大值限制的,即最大可保存124個(gè)字符,超出長(zhǎng)度則報(bào)數(shù)據(jù)越界異常。
public void encoderQRCoder(String content, HttpServletResponse response) { try { Qrcode handler = new Qrcode(); handler.setQrcodeErrorCorrect('M'); handler.setQrcodeEncodeMode('B'); handler.setQrcodeVersion(7); // System.out.println(content); byte[] contentBytes = content.getBytes("UTF-8"); BufferedImage bufImg = new BufferedImage(140, 140, BufferedImage.TYPE_INT_RGB); Graphics2D gs = bufImg.createGraphics(); gs.setBackground(Color.WHITE); gs.clearRect(0, 0, 140, 140); // 設(shè)定圖像顏色:BLACK gs.setColor(Color.BLACK); // 設(shè)置偏移量 不設(shè)置肯能導(dǎo)致解析出錯(cuò) int pixoff = 2; // 輸出內(nèi)容:二維碼 if (contentBytes.length > 0 && contentBytes.length < 124) { boolean[][] codeOut = handler.calQrcode(contentBytes); for (int i = 0; i < codeOut.length; i++) { for (int j = 0; j < codeOut.length; j++) { if (codeOut[j][i]) { gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3); } } } } else { System.err.println("QRCode content bytes length = " + contentBytes.length + " not in [ 0,120 ]. "); } gs.dispose(); bufImg.flush(); // 生成二維碼QRCode圖片 ImageIO.write(bufImg, "jpg", response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); } }
——————————————————————————————
// 字節(jié)長(zhǎng)度須控制在124個(gè)長(zhǎng)度以?xún)?nèi),否則報(bào)異常數(shù)組索引值溢出 String content = localIp + ":" + port + path + "/upload/toUploadMain.html?" + params; EncoderHandler encoder = new EncoderHandler(); encoder.encoderQRCoder(content, response);
所以content字符串拼接時(shí),注意長(zhǎng)度不得超出124個(gè)字節(jié),否則報(bào)錯(cuò),如下圖所示:
恩,以上內(nèi)容即為今天要講解的動(dòng)態(tài)生成二維碼并上傳資源的所有知識(shí)點(diǎn)了,各位猿人們多動(dòng)手操作幾次,相信你也能很好的學(xué)好一項(xiàng)小技術(shù),如有任何疑問(wèn)或建議的歡迎來(lái)博吐槽。
附件資源無(wú)法下載情況下,請(qǐng)自行挪步鏈接:down.51cto.com/data/2261528
如果各位童鞋還有任何建議或比較好的想法,歡迎加入JAVA開(kāi)發(fā)項(xiàng)目討論群:214404624。
發(fā)揮你聰智的大腦,挖掘更新鮮更充滿(mǎn)活力的好點(diǎn)子,我們共同探討技術(shù)層面的研究和可實(shí)施性。