1. 程式人生 > >H5實現webapp拍照或本地上傳

H5實現webapp拍照或本地上傳

<!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"));
	}
}
記錄一下,歡迎指正