H5實現webapp拍照或本地上傳
阿新 • • 發佈:2019-01-31
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/> <title>title</title> <script src="../js/jquery-1.8.3.min.js"></script> <script src="../js/mui.min.js"></script> <script src="../js/common.js"></script> <script src="../js/common-path.js"></script> <script src="../js/mui.picker.min.js"></script> <script src="../js/mui.poppicker.js"></script> <script type="text/javascript" src="../../resources/js/x-util.js"></script> <script type="text/javascript" src="../js/exif.js"></script> <link href="../css/mui.min.css" rel="stylesheet"/> <link href="../css/mui.picker.css" rel="stylesheet"/> <link href="../css/mui.poppicker.css" rel="stylesheet"/> <link href="../css/common.css" rel="stylesheet"/> <style> body{ background-color:#fff; } .pdtop{ margin-top:0; border-top:10px solid #f0f0f0; } .mui-bar .mui-btn-nav.mui-pull-right { margin-right: 5px; } .changImg { width: 75px; height: 75px; margin: 5px; float: left; } #imgBox { padding-left: 5px; background: #fff; overflow: hidden; } #file { width: 75px; height: 75px; display: block; opacity: 0; } .disChangImg{ width: 75px; height: 75px; margin: 5px; float: left; } </style> </head> <body> <header id="header" class="mui-bar mui-bar-nav"> <button id="subBtn" class="mui-btn mui-btn-nav mui-pull-right submit" onclick="submitFeedback()">提交</button> </header> <div class="nrcount"> <div class="pdtop"> <form class="mui-input-group" style="" id="feedbackForm" enctype="multipart/form-data"> <!-- 表單隱藏域 表單提交時傳遞的引數 start --> <input type="hidden" id="orientationStr" name="orientationStr"/> <!-- 表單隱藏域 表單提交時傳遞的引數 end --> </form> <!-- 圖片上傳 start 不放在form中的原因:每次選擇或拍照後再次選擇或拍照時,formdata都會清空只保留最後一次的資訊,所以傳到後臺的永遠是最後一張照片 修改後每次選擇或拍照時,通過type=file得到圖片並存入陣列中,最後將圖片陣列append到formdata中再傳到後臺去 --> <div class="mui-input-row upimg" style="background-color:#f0f0f0;"> <label>圖片上傳</label> <span class="mui-pull-right">非必要項</span> </div> <div class="basicAvatar"> <div id="imgBox"> <div class="Upload button changImg" id="morFile"> <input type="file" id="file" class="up" multiple="8" name="file" accept="image/*"> </div> <div class="Upload button disChangImg" id="disFile" style="display: none;" onclick="checkImgNum()"> </div> </div> </div> <!-- 圖片上傳 end --> </div> </div> <script> $(function(){ //判斷是Android還是iOS var ua = navigator.userAgent.toLowerCase(); var isiOS = (ua.indexOf('iphone') != -1) || (ua.indexOf('ipad') != -1); // ios終端 if(!isiOS){ //input type="file"對一些Android手機來說(如華為)並不能呼叫拍照功能 加上如下程式碼可呼叫手機攝像頭 $("input").attr('capture','camera'); } }) //刪除陣列中某一元素 function removeArrObj(_arr,_obj){ var length = _arr.length; for(var i = 0; i < length; i++){ if(_arr[i] == _obj){ if(i == 0){ _arr.shift(); //刪除並返回陣列的第一個元素 return; }else if(i == length-1){ _arr.pop(); //刪除並返回陣列的最後一個元素 return; }else{ _arr.splice(i,1); //刪除下標為i的元素 return; } } } } //圖片陣列 var fileArr=new Array(); //判斷上傳照片數量 function checkImgNum(){ if($(".xClass").length+$(".fileClass").length>=8){ mui.toast("最多上傳8張圖片"); $("#morFile").hide(); $("#disFile").show(); }else{ $("#morFile").show(); $("#disFile").hide(); } } //上傳圖片chang事件 $(".up").change(function () { let fileObj = $(this)[0].files; let windowURL = window.URL || window.webkitURL; let _box = document.getElementById("imgBox") let _button = document.getElementById("morFile") for(let item of fileObj) { if (fileObj.length < (9-$(".xClass").length-$(".fileClass").length)) { //圖片方向 var orientation; EXIF.getData(item, function(){ orientation = EXIF.getTag(this, 'Orientation'); let _div = document.createElement("div") _div.style.position = "relative" _div.style.float = "left" let _span = document.createElement("span") _span.style.position = "absolute" _span.style.right = 0 _span.style.top = 0 _span.style.width = "20px" _span.style.height = "20px" _span.style.zIndex = 1 _span.style.display = "block" _span.innerHTML = "x" _span.style.lineHeight = "17px" _span.style.backgroundColor = "#000" _span.style.color = "#fff" _span.style.textAlign = "center" _span.style.borderRadius = "10px" let _img = document.createElement("img") //壓縮圖片 var fileReader = new FileReader(); fileReader.onload = function(){ var IMG = new Image(); IMG.src = this.result; IMG.onload = function(){ var w = this.naturalWidth, h = this.naturalHeight, resizeW = 0, resizeH = 0; // maxSize 是壓縮的設定,設定圖片的最大寬度和最大高度,等比縮放,level是報錯的質量,數值越小質量越低 var maxSize = { width: 1000, height: 1000, level: 0.7 }; if(w > maxSize.width || h > maxSize.height){ var multiple = Math.max(w / maxSize.width, h / maxSize.height); resizeW = w / multiple; resizeH = h / multiple; var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); canvas.width = resizeW; canvas.height = resizeH; ctx.drawImage(IMG, 0, 0, resizeW, resizeH); var base64 = canvas.toDataURL('image/jpeg', maxSize.level); _span.classList.add("xClass") _span.setAttribute("base64info",base64)//設定屬性儲存圖片base64資訊 _span.setAttribute("orientation_blob",orientation)//設定屬性儲存圖片方向資訊(需要壓縮的) }else { //選擇的圖片存到陣列中 fileArr.push(item); _div.classList.add("fileClass") _div.setAttribute("orientation_file",orientation)//設定屬性儲存圖片方向資訊(無需壓縮的) } if($(".xClass").length+$(".fileClass").length>=8){ $("#morFile").hide(); $("#disFile").show(); } } }; fileReader.readAsDataURL(item); _img.classList.add("changImg") _span.addEventListener("click", function(){ //刪除圖片時 同時把陣列中的相應圖片刪除 removeArrObj(fileArr,item); $("#morFile").show(); $("#disFile").hide(); _box.removeChild(_div) }) _div.appendChild(_img) _div.appendChild(_span) let dataURL = windowURL.createObjectURL(item); _img.setAttribute('src', dataURL); _box.insertBefore(_div, _button) }); }else{ mui.toast("最多上傳8張圖片"); } } }) //將base64轉換為blob function convertBlob(base64){ var buffer = new ArrayBuffer(base64.length); var ubuffer = new Uint8Array(buffer); for (var i = 0; i < base64.length; i++) { ubuffer[i] = base64.charCodeAt(i) } var blob; try { blob = new Blob([buffer], {type: 'image/jpg'}); } catch (e) { window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if(e.name === 'TypeError' && window.BlobBuilder){ var blobBuilder = new BlobBuilder(); blobBuilder.append(buffer); blob = blobBuilder.getBlob('image/jpg'); } } return blob; } //提交方法 function submitFeedback(){ //提交按鈕禁用 $("#subBtn").attr("disabled","disabled"); //圖片方向字串 var orientationStr=""; //先存需要壓縮的 $(".xClass").each(function(index,item){ var orientation_blob=item.getAttribute("orientation_blob"); if(orientation_blob==null || orientation_blob=="" || typeof(orientation_blob)=="undefined" || orientation_blob=="undefined"){ orientation_blob=1; } orientationStr+=orientation_blob+','; }) //再存不需要壓縮的 $(".fileClass").each(function(index,item){ var orientation_file=item.getAttribute("orientation_file"); if(orientation_file==null || orientation_file=="" || typeof(orientation_file)=="undefined" || orientation_file=="undefined"){ orientation_file=1; } orientationStr+=orientation_file+','; }) if(orientationStr.length>0){ orientationStr=orientationStr.substring(0,orientationStr.length-1); $("#orientationStr").val(orientationStr); } var formData = new FormData($("#feedbackForm")[0]);//用form表單直接構造formData物件;此時formData中只包含文字資訊 //先存需要壓縮的即blob格式的 $(".xClass").each(function(index,item){ var base64=item.getAttribute("base64info"); if(base64!=null && base64!=""){ var blob=convertBlob(window.atob(base64.split(',')[1])); formData.append("file",blob); } }) //再存不需要壓縮的即file格式的 //將陣列中的圖片存到formData中 for(var i=0;i<fileArr.length;i++){ formData.append("file",fileArr[i]);//此時formData中既包含文字資訊又包含圖片資訊 } //ajax提交表單 $.ajax({ async: false,//要求同步 url : basepath + '/fileAction/fileUpload.do', type : 'POST', data : formData, processData : false, //必須false才會避開jQuery對formdata的預設處理 contentType : false, //必須false才會自動加上正確的Content-Type success : function(result){ //success } }); } </script> <!-- <script src="//cdn.jsdelivr.net/npm/eruda"></script> <script> //開啟手機除錯 eruda.init(); </script> --> </body> </html>
package com.xypt.action.mobile; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import com.xypt.entity.creditFeedback.CreditFeedback; import com.xypt.utils.ImageHelper; import com.xypt.utils.PropUtils; import com.xypt.utils.StringUtil; /** * 投訴或激勵 * * @author SKY 2018年5月23日 * @description * */ @RequestMapping("/fileAction") @Controller public class FileAction { @RequestMapping("/fileUpload") public void addCreditFeedbackInfo(@RequestParam(value = "file", required = false)MultipartFile[] file,CreditFeedback creditFeedback,String orientationStr,HttpServletRequest request, HttpServletResponse response) throws IOException { String orientationArr[]=orientationStr.split(","); List<Integer> orientationList=new ArrayList<Integer>(); for(String ori:orientationArr){ if(null!=ori && !"".equals(ori)){ orientationList.add(Integer.parseInt(ori)); } } for(int i=0;i<orientationList.size();i++){ MultipartFile multipartFile=file[i]; //獲取檔名 String fileName = multipartFile.getOriginalFilename(); if(null!=fileName && !"".equals(fileName)){ try { String fileExt=""; if(fileName.equals("blob")){ String fileType=multipartFile.getContentType(); System.out.println(fileType);//image/jpg fileExt = fileType.substring(fileType.lastIndexOf("/") + 1).toLowerCase();//副檔名 }else{ fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();//副檔名 } String newfilename = StringUtil.getUUID() + "." + fileExt; //生成uuid檔名 String savePath = "upload/images/tsjl/" + newfilename; File folder=new File(PropUtils.get("web.root")+"upload/images/tsjl"); if(!folder.exists()){ folder.mkdirs();//如果資料夾不存在 建立資料夾 } //旋轉圖片方向後上傳 ImageHelper.rotateAndUpload(multipartFile.getInputStream(), new File(PropUtils.get("web.root") + savePath), orientationList.get(i)); //資料庫儲存圖片路徑資訊 } catch (Exception e) { e.printStackTrace(); } } } } }
記錄一下,歡迎指正package com.xypt.utils; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.imageio.ImageIO; public class ImageHelper { /** * 實現影象的等比縮放 * * @param source * @param targetW * @param targetH * @return */ private static BufferedImage resize(BufferedImage source, int targetW, int targetH) { // targetW,targetH分別表示目標長和寬 int type = source.getType(); BufferedImage target = null; double sx = (double) targetW / source.getWidth(); double sy = (double) targetH / source.getHeight(); // 這裡想實現在targetW,targetH範圍內實現等比縮放。如果不需要等比縮放 // 則將下面的if else語句註釋即可 if (sx < sy) { sx = sy; targetW = (int) (sx * source.getWidth()); } else { sy = sx; targetH = (int) (sy * source.getHeight()); } if (type == BufferedImage.TYPE_CUSTOM) { // handmade ColorModel cm = source.getColorModel(); WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH); boolean alphaPremultiplied = cm.isAlphaPremultiplied(); target = new BufferedImage(cm, raster, alphaPremultiplied, null); } else target = new BufferedImage(targetW, targetH, type); Graphics2D g = target.createGraphics(); // smoother than exlax: g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy)); g.dispose(); return target; } /** * 實現縮放後的截圖 * * @param image * 縮放後的影象 * @param subImageBounds * 要擷取的子圖的範圍 * @param subImageFile * 要儲存的檔案 * @throws IOException */ private static void saveSubImage(BufferedImage image, Rectangle subImageBounds, File subImageFile) throws IOException { if (subImageBounds.x < 0 || subImageBounds.y < 0 || subImageBounds.width - subImageBounds.x > image.getWidth() || subImageBounds.height - subImageBounds.y > image.getHeight()) { // LoggerUtil.error(ImageHelper.class, "Bad subimage bounds"); return; } BufferedImage subImage = image.getSubimage(subImageBounds.x, subImageBounds.y, subImageBounds.width, subImageBounds.height); String fileName = subImageFile.getName(); String formatName = fileName.substring(fileName.lastIndexOf('.') + 1); ImageIO.write(subImage, formatName, subImageFile); } /** * <p>Title: Rotate</p> * <p>Description: 圖片旋轉方法</p> * @param src 圖片 * @param angel 旋轉角度 * @return */ public static BufferedImage Rotate(Image src, int angel) { int src_width = src.getWidth(null); int src_height = src.getHeight(null); Rectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel); BufferedImage res = null; res = new BufferedImage(rect_des.width, rect_des.height,BufferedImage.TYPE_INT_RGB); Graphics2D g2 = res.createGraphics(); g2.translate((rect_des.width - src_width) / 2,(rect_des.height - src_height) / 2); g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2); g2.drawImage(src, null, null); return res; } public static Rectangle CalcRotatedSize(Rectangle src, int angel) { if (angel >= 90) { if(angel / 90 % 2 == 1){ int temp = src.height; src.height = src.width; src.width = temp; } angel = angel % 90; } double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2; double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r; double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2; double angel_dalta_width = Math.atan((double) src.height / src.width); double angel_dalta_height = Math.atan((double) src.width / src.height); int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_width)); int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_height)); int des_width = src.width + len_dalta_width * 2; int des_height = src.height + len_dalta_height * 2; return new Rectangle(new Dimension(des_width, des_height)); } /** * <p>Title: rotateAndUpload</p> * <p>Description: 旋轉圖片方向並上傳</p> * @param in 輸入流 * @param saveFile 要儲存的圖片 * @param orientation 圖片方向 * @return */ public static boolean rotateAndUpload(InputStream in, File saveFile, int orientation) { if (null == in || null == saveFile) {//檢查引數有效性 return false; } //原始圖片 BufferedImage srcImage = null; //旋轉後的圖片 BufferedImage rotateImg=null; try { srcImage = ImageIO.read(in); int angle=0;//角度 if(6 == orientation ){ //6旋轉90 angle = 90; }else if( 3 == orientation){ //3旋轉180 angle = 180; }else if( 8 == orientation){ //8旋轉90 angle = 270; }else{ angle = 0; } //旋轉圖片方向 rotateImg = Rotate(srcImage, angle); String fileName = saveFile.getName();//檔名 String formatName = fileName.substring(fileName.lastIndexOf('.') + 1);//型別 try { ImageIO.write(rotateImg, formatName, saveFile); } catch (IOException e) { e.printStackTrace(); return false; } } catch (Exception e) { e.printStackTrace(); return false; } return true; } public static void main(String[] args) throws Exception { File file = new File("f:/111.jpg"); int orientation=6; int angle=0; if(6 == orientation ){ //6旋轉90 angle = 90; }else if( 3 == orientation){ //3旋轉180 angle = 180; }else if( 8 == orientation){ //8旋轉90 angle = 270; } BufferedImage src = ImageIO.read(file); BufferedImage des = Rotate(src, angle); ImageIO.write(des,"jpg", new File("f:/img/test.jpg")); } }