前言:
最近的開發中, 有一個上傳頭像的任務. 由於頭像本身的特殊性, 其一般流程為選擇圖片, 編輯裁剪區域, 再繼而上傳圖片操作.
看似簡單的東西, 實則是挺麻煩的一件事. 借助這次開發機會, 來具體談談圖片裁剪和文件異步上傳的基本原理.
技術點:
由於采用springmvc作為web的mvc框架, 因此文件上傳的基礎知識,可參考如下博文.
• springmvc學習筆記--支持文件上傳和阿里雲OSS API簡介.
處理模式:
先來看看頭像編輯和上傳的一些案例吧.

先上傳圖片, 然后選擇裁剪區域, 然后點擊完成. 此時圖片就會異步上傳, 並最終返回image url.
這邊涉及的問題包括如下:
1). 圖片文件的異步上傳如何實現?
2). 圖片的裁剪過程在哪里完成, 客戶端還是服務端?
帶着這些疑問, 我們來講述下后續的文章吧.
文件異步上傳原理:
眾所周知, 圖片是以multipart/form-data的表單模式進行上傳的. 這種模式是不支持異步刷新的, 或者確切地說不支持Ajax機制.
當然有需求, 就會有人前呼后繼去嘗試各種解決方案. 當前主流的思路是, 借助iframe來模擬實現無刷新的文件上傳.
動態生成一個臨時的iframe, 然后把需要post的目標轉移到該iframe. iframe請求結束后, 主頁面(父頁面)提取iframe的返回結果, 並進行相應的更新.
具體可以參考下博文: jquery插件--ajaxfileupload.js上傳文件原理分析.
頭像裁剪的原理:
回到之前的一個疑問: 圖片的裁剪是在客戶端完成, 還是在服務端完成呢?
我們借助一個cropper插件的一個實例demo, 用chrome控制台工具來檢測一下.
點擊一下, 可訪問cropper的插件站點.
可以發現其post表單中, 存在avatar_data字段, 其指定了裁剪范圍(x, y, width, height, rotate).
------WebKitFormBoundaryAuaPsyCAjglAoLNd
Content-Disposition: form-data; name="avatar_data"
{"x":19.999999999999996,"y":-9.969788519637461,"height":160,"width":160,"rotate":0}
當然還存在完整的圖片數據, 為avatar_file字段.
------WebKitFormBoundaryAuaPsyCAjglAoLNd
Content-Disposition: form-data; name="avatar_file"; filename="tu17250_14.jpg"
Content-Type: image/jpeg
由此可見, 圖片的裁剪工作是由服務器端完成的, 客戶端只是設定了裁剪信息.
圖片處理:
這邊也羅列一下圖片處理的java代碼片段吧.
主要包括裁剪, 縮放等操作.
• 裁剪操作:
/**
*
* @param srcImage
* @param x
* @param y
* @param dw
* @param dh
* @return
*/
public static BufferedImage cutoffImage(
BufferedImage srcImage, double x, double y, double dw, double dh) {
int width = srcImage.getWidth();
int height = srcImage.getHeight();
double sx = Math.min(Math.max(0, x), width);
double sy = Math.min(Math.max(0, y), height);
double dx = Math.min(Math.max(0, x + dw), width);
double dy = Math.min(Math.max(0, y + dh), height);
BufferedImage subImage = srcImage.getSubimage(
(int)sx, (int)sy, (int)(dx - sx), (int)(dy - sy)
);
return subImage;
}
• 縮放操作:
/**
*
* @param srcImage 源圖片
* @param dstWidth 目標圖片的寬
* @param dstHeight 目標圖片的高
* @return
*/
public static BufferedImage zoomImage(BufferedImage srcImage, int dstWidth, int dstHeight) {
double wr = dstWidth * 1.0 / srcImage.getWidth();
double hr = dstHeight * 1.0 / srcImage.getHeight();
AffineTransformOp ato = new AffineTransformOp(
AffineTransform.getScaleInstance(wr, hr), null
);
return ato.filter(srcImage, null);
}
對於java的圖片處理, 其實可挖掘的點很多, 這邊簡單寫寫.
總結:
很多人覺得, 插件集成很容易, 實則不是, 里面需要做很多工作, 對原理的了解和如何交互集成, 都需要費不少勁.
本文簡單講述了下圖片裁剪和上傳的基本原理, 相對還是比較水. 希望有一天, 能夠有機會對java的圖像處理, 做一個深入的理解, 包括性能和處理模式.
公眾號&游戲站點:
個人微信公眾號: 木目的H5游戲世界

個人游戲作品集站點(尚在建設中...): www.mmxfgame.com, 也可直接ip訪問: http://120.26.221.54/.
