我們到底能走多遠系列(20)
扯淡:12月15號預訂的mx2,現在還沒貨,唉悲劇。本來想參加朋友婚禮前換個手機的,看來是指望不上了,朋友結婚,5星級酒店,那酒店我高中的時候每天會望望它的樓頂有沒有盤旋的直升機,總算咋也能進去吃個飯喝個酒啦,咋農民也就這么點追求,哈哈。
主題:
圖片上傳,預覽,剪切在很多網站上都會使用,可是到自己真正想做的時候,卻遇到了各種困難很問題,總算也是完整方案出來啦。分享一下。
表現流程如下:
步驟1:點擊瀏覽按鈕選中圖片-------1------>onchange事件把圖片上傳到服務器-------2----->圖片路徑回傳
步驟2:進入切割圖片------3----->切割,取得坐標,長度--------4---->傳給服務器---------->后台切割產生新圖片-----5------>回傳新圖路徑
步驟3:頁面截圖預覽
步驟1:
file標簽:
<input name="advImage" id="advImage" type="file" style="height:22px;" onchange="uploadImage()"/>
onchange事件調用的js方法:
ajaxFileUpload利用iframe模擬了ajax上傳文件。
url:"uploadPreviewImage.html" 就是后台地址,(本人使用的是spring mvc),
success:function (data , status),上傳成功后調用的js中,$('#photo').imgAreaSelect方法是使用了imgAreaSelect插件來初始化截圖界面。
官網地址:http://odyniec.net/projects/imgareaselect/
關於ajaxFileUpload的api可以查看官網,開始的時候我使用的是網上隨便下的一個js,發現一直調不同,最后換了官網的,才ok。
function uploadImage(){ // 檢查圖片格式 var f=document.getElementById("advImage").value; if(!/\.(gif|jpg|jpeg|png|JPG|PNG)$/.test(f)) { alert("圖片類型必須是.jpeg,jpg,png中的一種") return false; } // 利用ajaxFileUpload js 插件上傳圖片 $.ajaxFileUpload({url:"uploadPreviewImage.html", secureuri:false, fileElementId:"advImage", dataType:"json", success:function (data , status) { //上傳成功后,直接跳出截圖框,使用imgAreaSelect插件 piso = $('#photo').imgAreaSelect({ x1: 0, y1: 0, x2:480 , y2: 520 ,onSelectEnd: preview, resizable: false, instance: true, persistent:true }); // 這個方法是現實一個div,托住截圖框 showCutImage(); // 一些變量在頁面的隱藏input的設置 document.getElementById("photo").src = data.tempPath; document.getElementById("currentPath").value = data.tempPath; }, error:function (data, status, e) { //alert("圖片上傳失敗,請重新選擇圖片"); } }); return false; } // 截圖選中后調用方法,保存好起始坐標和寬高 function preview(img, selection) { $('#x1').val(selection.x1); $('#y1').val(selection.y1); $('#x2').val(selection.x2); $('#y2').val(selection.y2); $('#w').val(selection.width); $('#h').val(selection.height); }
uploadPreviewImage方法,把文件方法零時文件夾下:
public ModelAndView uploadPreviewImage(HttpServletRequest request, HttpServletResponse response) throws IOException{ User user = (User)request.getSession().getAttribute("user"); MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request; MultipartFile image = multipartRequest.getFile("advImage"); response.setCharacterEncoding(BusinessConstants.ENCOD_UTF); response.setHeader("ContentType", "json"); PrintWriter out = response.getWriter(); // 組合零時圖片名 String imageName = image.getOriginalFilename(); String file_ext = imageName.substring(imageName.lastIndexOf(BusinessConstants.DOT) + 1); SimpleDateFormat df = new SimpleDateFormat(BusinessConstants.DATE_FORMAT); String tempImageName = user.getId() +BusinessConstants.UNDERLINE + df.format(new Date()) + BusinessConstants.DOT + file_ext; // 存放瀏覽圖片的零時文件路徑 File file = new File(request.getSession().getServletContext().getRealPath(BusinessConstants.TEMP_PICTURE_PATH + tempImageName)); byte[] advImageBytes = null; InputStream advImageStream = null; try { file.createNewFile(); advImageStream = image.getInputStream(); advImageBytes = FileCopyUtils.copyToByteArray(advImageStream); FileCopyUtils.copy(advImageBytes, file); advImageStream.close(); } catch (IOException e) { e.printStackTrace(); } String tempPath = BusinessConstants.TEMP_RELATIVE_PICTURE_PATH + tempImageName; // 傳給頁面相對路徑 out.print("{"); out.print("tempPath:'"+tempPath+"',"); out.print("msg:''"); out.print("}"); out.flush(); out.close(); // ajax return null; }
上面的uploadPreviewImage調用完成后就會把下面的div顯示出來(初始隱藏):
這就是截圖界面啦!
<div id="cutImageDiv" class="displayCutDiv" > <div id="cutImageShut" class="amend-close">關閉</div> <div style="margin: 0 auto;"><img src="../images/test.jpg" id="photo"onclick="imgAreaSelect();"/> <input id="x1" type="hidden" /> <input id="x2" type="hidden" /> <input id="y1" type="hidden" /> <input id="y2" type="hidden" /> <input id="w" type="hidden" /> <input id="h" type="hidden" /> </div> <input type="button" id="cutImage" name="cutImage" class="btton-queren" onclick="cutImage()" style="float: right;" value="保存"/> </div>
步驟2:
我們可以看到截圖完畢后點擊保存,就會調用cutImage方法:
里面我們利用dwr調用ReleaseService的cutImage方法。
function cutImage(){ var list = new Array(); list[0] = $("#x1").val(); list[1] = $("#x2").val(); list[2] = $("#y1").val(); list[3] = $("#y2").val(); list[4] = $("#w").val(); list[5] = $("#h").val(); var currentPath = document.getElementById("currentPath").value; // 這里是利用的dwr框架直接調用后台方法,以及使用后台傳回的值 // 這個方法就是利用坐標寬高進行切圖,事實上這時候的原圖已經在服務器了,所以我們只需要知道他的相對路徑,即currentPath ReleaseService.cutImage(list, currentPath, function(value){ document.getElementById("currentPath").value = value; } ); var bgObj=document.getElementById("bgDiv"); var msgObj=document.getElementById("cutImageDiv"); bgObj.style.display = msgObj.style.display = "none"; piso.cancelSelection(); haveImage = 1; $('#msHaveImage').show(); //$(".imgareaselect-outer").hide(); //$(".imgareaselect-selection").parent().hide(); }
ReleaseService的cutImage方法:
public String cutImage(int[] size, String path) { int x1 = size[0]; int x2 = size[1]; int y1 = size[2]; int y2 = size[3]; int w = size[4]; int h = size[5]; if(w <= 0) w = 480; if(h<=0) h = 520; if(x1<0) x1 = 0; if(y1<0) y1 = 0; //File file = new File(request.getSession().getServletContext().getRealPath(path)); path = path.substring(2); WebContext ctx = WebContextFactory.get(); User user = (User)ctx.getSession().getAttribute("user"); SimpleDateFormat df = new SimpleDateFormat(BusinessConstants.DATE_FORMAT); String file_ext = path.substring(path.lastIndexOf(BusinessConstants.DOT) + 1); File file = new File(ctx.getSession().getServletContext().getRealPath(path)); String imageName = user.getId() + BusinessConstants.UNDERLINE + df.format(new Date()) + BusinessConstants.DOT + file_ext; String newFile = ctx.getSession().getServletContext().getRealPath(BusinessConstants.TEMP_PICTURE_PATH + imageName); //切圖操作 ImageCut.abscut(ctx.getSession().getServletContext().getRealPath(path), newFile, x1, y1, w, h); return BusinessConstants.TEMP_RELATIVE_PICTURE_PATH + imageName; }
來看一下截圖方法:主要是Graphics2D的drawImage方法。
public static void abscut(String srcImageFile,String desImageFile, int x, int y,int width, int height) { try { Image img; ImageFilter cropFilter; File srcFile = new File(srcImageFile); //String fileName = srcFile.getName(); String ext = getExtension(srcImageFile); if(ext==null)ext="jpg"; // 讀取源圖像 BufferedImage bi = ImageIO.read(srcFile); int srcWidth = bi.getWidth(); // 源圖寬度 int srcHeight = bi.getHeight(); // 源圖高度 if (srcWidth >= width && srcHeight >= height) { BufferedImage tag; Image image = bi.getScaledInstance(srcWidth, srcHeight,Image.SCALE_DEFAULT); // 四個參數分別為圖像起點坐標和寬高 // 即: CropImageFilter(int x,int y,int width,int height) cropFilter = new CropImageFilter(x, y, width, height); img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter)); int type = BufferedImage.TYPE_INT_RGB; if("gif".equalsIgnoreCase(ext)||"png".equalsIgnoreCase(ext)){ type = BufferedImage.TYPE_INT_ARGB; } tag = new BufferedImage(width, height,type); Graphics2D g = (Graphics2D)tag.getGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(img, 0, 0, null); // 繪制剪切后的圖 g.dispose(); ImageIO.write(tag,ext, new File(desImageFile)); srcFile.delete();//刪除原圖 } } catch (Exception e) { e.printStackTrace(); } } public static String getExtension(String srcImageFile) { String ext = null; if(srcImageFile!=null && srcImageFile.lastIndexOf(".")>-1){ ext = srcImageFile.substring(srcImageFile.lastIndexOf(".")+1); } return ext; }
步驟3:
至此,截圖文件就被保存到指定的文件夾下,相對路徑也會被存到數據庫中,在頁面上的處理就是把需要展現的img轉變成src接可以完成了。
補充: 因為以上的流程會導致零時文件夾的廢棄文件(比如用戶上傳了原圖,卻放棄截圖),座椅考慮在linux,上寫一個定時腳本,來刪除這個零時文件夾下的圖片。
一下是遺留問題,希望園子里的朋友能夠給一點意見或提示:
問題:
1,使用imgareaselect的時候使用截圖框固定的形式,如果原圖比這個固定框小,就會出現js錯誤。是否解決方案?
2,截圖程序中利用Graphics2D的drawImage方法來畫圖,耗時過長,ajax效果的就要延遲體現,無法滿足截圖后馬上顯示效果。這個方面不知道怎么改進?
好吧 我不理解園子的朋友指的圖是不是這個,姑且認為是效果圖吧
上一張剪切圖片時候的圖片:
有興趣的幫我解答下我遺留的問題哦,謝啦。
萬水千山總是情,也許您可以支持一下,謝謝!
讓我們繼續前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不會成功。
共勉。