修改頭像總結
1,背景
博客停了好久,主要是最近工作太忙了,還有就是身體狀況沒有以前那么好了,乘著國慶長假的空檔,寫下這篇一直想寫的文章。
運營平臺是我主要致力的一個項目,這個項目分為四個大部分,個人中心,充值中心,客服中心,家長監護,最近主要忙著個人中心的重寫和豐富,關于個人中心,無非就是對平臺用戶信息的自我管理,以及一些對用戶帳號的安全保護措施,下圖的菜單非常簡要的說明了個人中心的功能。個人覺得最值得關注的就是密保設置和修改頭像,因為之前沒有處理過類似的問題,本文主要記錄對頭像的處理過程以及思考,希望給碰到類似問題的苦逼程序員一點借鑒。

個人中心整體功能一覽
2,頭像處理xmind
嘰歪一句,個人碰到問題的時候,首先會分析問題,在分析問題的基礎上,得到整體的解決方案,然后一步步分解步驟,去實現,首先奉上我的解決方案,也許不是最優的,但是按照個人的知識和技能水平,絕對是可以實現的。

修改頭像mind
3,實現步驟
按照我的mind,首先是上傳圖片,先上效果圖,然后給出實現的代碼。首先是整體的結構圖,做的比較丑,別噴哥···
修改頭像整體效果圖

下面按照mind一步步實現,
首先:點擊修改頭像,彈出一個層,

第一步:彈出上傳圖片的層,上傳圖片到服務器
對實現細節不感冒的屌絲可以看看代碼(結合哥的mind看可以事半功倍):
分層 實現細節
Html結構層 這個可以免了,一般都可以弄出來
Js連接層
首先是彈出一個上傳圖片的層,然后上傳圖片到服務器端。 $("#editHead").bind("click", function () {
showUploadDiv();
});
function showUploadDiv() {
$("#uploadMsg").empty();
$.fancybox({
type:'inline',
width:400,
href:'#uploadUserHead'
});
}//fancybox彈出層
上傳的處理代碼
Servlet服務端處理層(commonupload實現) 服務器端處理代碼
上傳的處理代碼
$(function () {
$("#uploadFrom").ajaxForm({
beforeSubmit:checkImg,
error:function(data,status){
alert(status+' , '+data);
$("#uploadMsg").html('上傳文件超過1M!');
},
success:function (data,status) {
try{
var msg = $.parseJSON(data);
if (msg.code == 200)
{ //如果成功提交
javascript:$.fancybox.close();
$("#uploadUserHead").hide();
var data = msg.object;
$("#editImg").attr("src", data.path).show();
$("#preview1").attr("src", data.path).show();
$(".zoom").show();
$("#width").val(data.width);
$("#height").val(data.height);
$("#oldImgPath").val(data.realPath);
$("#imgFileExt").val(data.fileExt);
var api, jcrop_api, boundx, boundy;
$('#editImg').Jcrop({
onChange:updatePreview,
onSelect:updatePreview,
aspectRatio:1,
bgOpacity:0.5,
bgColor:'white',
addClass:'jcrop-light'
}, function () {
api = this;
api.setSelect([130, 65, 130 + 350, 65 + 285]);
api.setOptions({ bgFade:true });
api.ui.selection.addClass('jcrop-selection');
var bounds = this.getBounds();
boundx = bounds[0];
boundy = bounds[1];
jcrop_api = this;
});
function updatePreview(c) {
if (parseInt(c.w) > 0) {
var rx = 80 / c.w;
var ry = 80 / c.h;
$('#preview1').css({
width:Math.round(rx * boundx) + 'px',
height:Math.round(ry * boundy) + 'px',
marginLeft:'-' + Math.round(rx * c.x) + 'px',
marginTop:'-' + Math.round(ry * c.y) + 'px'
});
}
jQuery('#x').val(c.x);
jQuery('#y').val(c.y);
jQuery('#x2').val(c.x2);
jQuery('#y2').val(c.y2);
jQuery('#w').val(c.w);
jQuery('#h').val(c.h);
}
}
if (msg.code == 204) {
$("#uploadMsg").html(msg.msg);
}
}catch (e){
$("#uploadMsg").html('上傳文件超過1M!');
}
}
});
});
//服務器端處理代碼
String tempSavePath = ConfigurationUtils.get("user.resource.dir"); //上傳的圖片零時保存路徑
String tempShowPath = ConfigurationUtils.get("user.resource.url"); //用戶保存的頭像路徑
if(tempSavePath.equals("/img"))
{
tempSavePath=sc.getRealPath("/")+tempSavePath;
}
Msg msg = new Msg();
msg.setCode(204);
msg.setMsg("上傳頭像失敗!");
String type = request.getParameter("type");
if (!Strings.isNullOrEmpty(type) && type.equals("first")) {
request.setCharacterEncoding("utf-8");
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
try {
List items = servletFileUpload.parseRequest(request);
Iterator iterator = items.iterator();
while (iterator.hasNext()) {
FileItem item = (FileItem) iterator.next();
if (!item.isFormField()) {
{
File tempFile = new File(item.getName());
File saveTemp = new File(tempSavePath+"/tempImg/");
String getItemName=tempFile.getName();
String fileName = UUID.randomUUID()+"." +getItemName.substring(getItemName.lastIndexOf(".") + 1, getItemName.length());
File saveDir = new File(tempSavePath+"/tempImg/", fileName);
//如果目錄不存在,創建。
if (saveTemp.exists() == false) {
if (!saveTemp.mkdir()) { // 創建失敗
saveTemp.getParentFile().mkdir();
saveTemp.mkdir();
} else {
}
}
if (saveDir.exists()) {
log.info("存在同名文件···");
saveDir.delete();
}
item.write(saveDir);
log.info("上傳頭像成功!"+saveDir.getName());
msg.setCode(200);
msg.setMsg("上傳頭像成功!");
Image image = new Image();
BufferedImage bufferedImage = null;
try {
bufferedImage = ImageIO.read(saveDir);
} catch (IOException e) {
e.printStackTrace();
}
image.setHeight(bufferedImage.getHeight());
image.setWidth(bufferedImage.getWidth());
image.setPath(tempShowPath+ "/tempImg/" + fileName);
log.info(image.getPath());
image.setRealPath(tempSavePath+"/tempImg/"+ fileName);
image.setFileExt(fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()));
msg.setObject(image);
}
} else {
log.info("" + item.getFieldName());
}
}
} catch (Exception ex) {
log.error("上傳用戶頭像圖片異常!");
ex.printStackTrace();
}
finally {
AppHelper.returnJsonAjaxForm(response, msg);
}
}
上傳成功后,可以看到照片和照片的預覽效果。看圖:

上傳頭像之后的效果
Friday, October 05, 2012第二步:編輯和保存頭像
選中圖中的區域,保存頭像,就完成頭像的修改。
修改之后的效果入下:
修改之后的頭像(因為傳了一張動態圖片,得到的跟上圖有些不同)
實現細節:
首先用了一個js控件:Jcrop,有興趣的屌絲可以去搜一下,然后,利用上傳之后的圖片和之前的選定區域,完成了一個截圖,保存為用戶的頭像。
連接層的js:
$("#saveHead").bind("click", function () {
var width = $("#width").val();
var height = $("#height").val();
var oldImgPath = $("#oldImgPath").val();
var imgFileExt = $("#imgFileExt").val();
var x = $('#x').val();
var y = $('#y').val();
var w = $('#w').val();
var h = $('#h').val();
$.ajax({
url:'/imgCrop',
type:'post',
data:{x:x, y:y, w:w, h:h, width:width, height:height, oldImgPath:oldImgPath, fileExt:imgFileExt},
datatype:'json',
success:function (msg) {
if (msg.code == 200) {
$("#avatar").attr("src", msg.object);
forword('/nav', 'index');
}
else {
alert(msg.msg);
}
}
});
});
function checkImg() {
//限制上傳文件的大小和后綴名
var filePath = $("input[name='uploadImg']").val();
if (!filePath) {
$("#uploadMsg").html("請選擇上傳文件!").show();
return false;
}
else {
var extStart = filePath.lastIndexOf(".");
var ext = filePath.substring(extStart, filePath.length).toUpperCase();
if (ext != ".PNG" && ext != ".GIF" && ext != ".JPG") {
$("#uploadMsg").html("圖片限于png,gif,jpg格式!").show();
return false;
}
}
return true;
}
服務器端處理代碼:
String savePath = ConfigurationUtils.get("user.resource.dir"); //上傳的圖片保存路徑
String showPath = ConfigurationUtils.get("user.resource.url"); //顯示圖片的路徑
if(savePath.equals("/img"))
{
savePath=sc.getRealPath("/")+savePath;
}
int userId = AppHelper.getUserId(request);
String userName=AppHelper.getUserName(request);
Msg msg = new Msg();
msg.setCode(204);
msg.setMsg("剪切圖片失敗!");
if (userId <= 0) {
msg.setMsg("請先登錄");
return;
}
// 用戶經過剪輯后的圖片的大小
Integer x = (int)Float.parseFloat(request.getParameter("x"));
Integer y = (int)Float.parseFloat(request.getParameter("y"));
Integer w = (int)Float.parseFloat(request.getParameter("w"));
Integer h = (int)Float.parseFloat(request.getParameter("h"));
//獲取原顯示圖片路徑 和大小
String oldImgPath = request.getParameter("oldImgPath");
Integer width = (int)Float.parseFloat(request.getParameter("width"));
Integer height = (int)Float.parseFloat(request.getParameter("height"));
//圖片后綴
String imgFileExt = request.getParameter("fileExt");
String foldName="/"+ DateUtils.nowDatetoStrToMonth()+"/";
String imgName = foldName + UUID.randomUUID()+userName + "." + imgFileExt;
//組裝圖片真實名稱
String createImgPath = savePath + imgName;
//進行剪切圖片操作
ImageCut.abscut(oldImgPath,createImgPath, x*width/300, y*height/300, w*width/300, h*height/300);
File f = new File(createImgPath);
if (f.exists()) {
msg.setObject(imgName);
//把顯示路徑保存到用戶信息下面。
UserService userService = userServiceProvider.get();
int rel = userService.updateUserAvatar(userId, showPath+imgName);
if (rel >= 1) {
msg.setCode(200);
msg.setMsg("剪切圖片成功!");
log.info("剪切圖片成功!");
//記錄日志,更新session
log(showPath+imgName,userName);
UserObject userObject= userService.getUserObject(userName);
request.getSession().setAttribute("userObject", userObject);
if (userObject != null && Strings.isNullOrEmpty(userObject.getHeadDir()))
userObject.setHeadDir("/images/geren_right_01.jpg");
} else {
msg.setCode(204);
msg.setMsg("剪切圖片失敗!");
log.info("剪切圖片失敗!");
}
}
AppHelper.returnJson(response, msg);
File file=new File(oldImgPath);
boolean deleteFile= file.delete();
if(deleteFile==true)
{
log.info("刪除原來圖片成功");
}
/**
* 圖像切割(改) *
*
* @param srcImageFile 源圖像地址
* @param dirImageFile 新圖像地址
* @param x 目標切片起點x坐標
* @param y 目標切片起點y坐標
* @param destWidth 目標切片寬度
* @param destHeight 目標切片高度
*/
public static void abscut(String srcImageFile, String dirImageFile, int x, int y, int destWidth, int destHeight) {
try {
Image img;
ImageFilter cropFilter;
// 讀取源圖像
BufferedImage bi = ImageIO.read(new File(srcImageFile));
int srcWidth = bi.getWidth(); // 源圖寬度
int srcHeight = bi.getHeight(); // 源圖高度
if (srcWidth >= destWidth && srcHeight >= destHeight) {
Image image = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
// 改進的想法:是否可用多線程加快切割速度
// 四個參數分別為圖像起點坐標和寬高
// 即: CropImageFilter(int x,int y,int width,int height)
cropFilter = new CropImageFilter(x, y, destWidth, destHeight);
img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(img, 0, 0, null); // 繪制縮小后的圖
g.dispose();
// 輸出為文件
ImageIO.write(tag, "JPEG", new File(dirImageFile));
}
} catch (Exception e) {
e.printStackTrace();
}
}
最后一個處理的比較好的地方就是圖片的存儲路徑問題:
我在服務器端的nginx中做了一個圖片的地址映射,把圖片放到了跟程序不同的路徑中,每次存儲圖片都是存到圖片路徑中,客戶端拿到圖片的地址確實經過nginx映射過的地址。
還有就是關于限制上傳圖片的大小的問題:
我在服務器端顯示了資源的最大大小為1M,當上傳的資源超過1M,服務器自動報錯413,通過異常處理,可以在客戶端得到正確的提示信息。
4,總結優點和不足。
關于修改頭像,這么做下來確實達到了目的,用戶可以從容的修改頭像,性能也還可以。但是,上傳圖片的大小判斷是依靠服務器端來判斷的,等待的時間比較久,改進的方向是使用flash控件來限制,使用flash來上傳,也不會出現彈出層,這樣比較大眾化,更容易為用戶接受一點。我會不斷改進。
no pays,no gains!

浙公網安備 33010602011771號