前台利用jcrop做头像选择预览,后台通过django利用Uploadify组件上传图最终使用PIL做图像裁切
之前一直使用python的PIL自定义裁切图片,今天有需求需要做一个前端的选择预览页面,索性就把这个功能整理一下,分享给大家。
实现思路:
1、前端页面:
用户选择本地一张图片,然后通过鼠标缩放和移动,确定自己所需要的图片切块,最终把图片切块的 左边距,上边距,长,宽这些个参数传给后台
2、后台:
使用的django,主要实现2部分的功能,第一:图片上传,第二:图片裁切
先看一张图片:
前端页面:
后台最后得到的图片:
对于该demo中,我用到了以下js插件:
jquery-webox:弹出图层(你可以不关心)
jcrop:在线裁切预览图片 http://deepliquid.com/content/Jcrop_Implementation_Theory.html
jquery.uploadify:上传附件
html页面:
a)用户信息页面:userinfo.html
b)弹出页面用于用户选择、上传、预览图片:index.html
django程序:
UploadImage模块下有以下几个文件:
c)urls.py
d)views.py
下面就开始贴代码了
a)的代码:
{% extends "kidcrate/base.html" %} {%block contentBar%} <link href="/site_media/uploadify/uploadify.css" type="text/css" rel="stylesheet" /> <script type="text/javascript" src="/site_media/uploadify/jquery.uploadify.min.js"></script> <script type="text/javascript" src="/site_media/js/thickbox.js"></script> <link rel='stylesheet' type='text/css' href='/site_media/css/thickbox.css' /> <link href="/site_media/common/jquery_webox/jquery-webox.css" rel="stylesheet" type="text/css"> <script src="/site_media/common/jquery_webox/jquery-webox.js"></script> <script type="text/javascript"> $(document).ready(function(){ //iframe弹出层调用 $('#outside').click(function(){ $.webox({ height:500, width:800, bgvisibel:true, title:'修改头像', iframe:'/uploadify?uuid='+$('#uuid').val()+'&rd='+Math.random() }); }); }) </script> <DIV class="yyh-page grid_9"> <DIV class=widget> <DIV class=widget-content> <!-- basic form --> <FORM id=basic class="yyh-form tabs-rel tabs-info on" method=post action="/account/userinfo/"> <input type="hidden" id="uuid" name="uuid" value="{{uuid}}" /> <DL class=required> <DT> <LABEL for=producer> 账号 </LABEL> </DT> <DD> <INPUT class=inputText style="color: #400080;font - size: 16px;" readonly="true" value="{{username}}"> <SPAN> <B class=error> </B> </SPAN> <P class=hint> </P> </DD> <DT> <LABEL for=producer> 联系人真实姓名 </LABEL> </DT> <DD> <INPUT class=inputText name=devName value="{{devName}}"> <SPAN> <B class=error> </B> </SPAN> <P class=hint> </P> </DD> </DL> <DL class=required> <DT> <LABEL for=phone> 联系电话 </LABEL> </DT> <DD> <INPUT class=inputText name=contactPhone value="{{contactPhone}}"> <SPAN> <B class=error> </B> </SPAN> <P class=hint> </P> </DD> </DL> <DL class=required> <DT> <LABEL for=address> 联系地址 </LABEL> </DT> <DD> <INPUT class=inputText name=contactAddress value="{{contactAddress}}"> <SPAN> <B class=error> </B> </SPAN> <P class=hint> 请填写真实的联系地址 </P> </DD> </DL> <DL class=required> <DT> <LABEL for=zipcode> 邮政编码 </LABEL> </DT> <DD> <INPUT class="inputText NumberValidate" name=contactZipCode value="{{contactZipCode}}"> <SPAN> <B class=error> </B> </SPAN> </DD> </DL> <DL> <DT> <LABEL for=headimg> 头像 </LABEL> </DT> <DD> <img id="screenshot_img" name="screeshot_img" src="/site_media/images/account_head/femail.jpg" width="120" height="120" /><br><br/> <div id="upload_div" style="display:visible;"> <a class="Button blueButton Button18" href="javascript:void(0);" id="outside"><strong>点我修改</strong></a> </div> </DD> </DL> <DL class=submit> <DT> </DT> <DD> <INPUT class="inputSubmit blue" value=保存 type=button onclick="this.form.submit(); "> </DD> </DL> </FORM> <!-- end of basic form --> </DIV> </DIV> </DIV> {%endblock%}"
b)的代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Django下利用Uploadify组件上传图片</title> <link rel="stylesheet" href="/site_media/jcrop/demos/demo_files/main.css" type="text/css" /> <link rel="stylesheet" href="/site_media/jcrop/demos/demo_files/demos.css" type="text/css" /> <link rel="stylesheet" href="/site_media/jcrop/css/jquery.Jcrop.css" type="text/css" /> <script src="/site_media/jcrop/js/jquery.min.js"></script> <script src="/site_media/jcrop/js/jquery.Jcrop.js"></script> <script type="text/javascript"> var img_top_margin,img_left_margin,img_width,img_height;//最后使用的2个变量 jQuery(function($){ // Create variables (in this scope) to hold the API and image size var jcrop_api, boundx, boundy, topw, leftw, // Grab some information about the preview pane $preview = $('#preview-pane'), $pcnt = $('#preview-pane .preview-container'), $pimg = $('#preview-pane .preview-container img'), xsize = $pcnt.width(), ysize = $pcnt.height(); console.log('init',[xsize,ysize]); $('#target').Jcrop({ onChange: updatePreview, onSelect: updatePreview, aspectRatio: xsize / ysize },function(){ // Use the API to get the real image size var bounds = this.getBounds(); boundx = bounds[0]; boundy = bounds[1]; // Store the API in the jcrop_api variable jcrop_api = this; // Move the preview into the jcrop container for css positioning $preview.appendTo(jcrop_api.ui.holder); }); function updatePreview(c) { if (parseInt(c.w) > 0) { var rx = xsize / c.w; var ry = ysize / c.h; console.log("new width:"+Math.round(rx * boundx) ); console.log("new height:"+Math.round(ry * boundy) ); console.log("marginTop:"+Math.round(ry * c.y)); console.log("marginLeft:"+Math.round(rx * c.x)); img_top_margin=c.y; img_left_margin=c.x; img_width=c.w; img_height=c.h; $pimg.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' }); } }; }); </script> <link href="/site_media/uploadify/uploadify.css" type="text/css" rel="stylesheet" /> <script type="text/javascript" src="/site_media/uploadify/swfobject.js"></script> <script type="text/javascript" src="/site_media/uploadify/jquery.uploadify.v2.1.4.min.js"></script> <script type="text/javascript" charset="utf-8" async defer> function go() { alert(2); } function replace_image(flag,path,uuid) { console.log("==========replace_image======="); console.log("marginTop:"+img_top_margin); console.log("marginLeft:"+img_left_margin); console.log("width:"+img_width); console.log("height:"+img_height); var params="&marginTop="+img_top_margin+"&marginLeft="+img_left_margin+"&width="+img_width+"&height="+img_height; $.ajax({ type: "GET", url: "?replace_flag="+flag+"&savepath="+path+params, dataType: "json", success: function (json) { alert(json.message); //document.getElementById("btnquery").click(); document.getElementById("echo_href_msg").innerHTML = json.message; } }) } </script> <script type="text/javascript"> $(document).ready(function() { $('#file_upload').uploadify({ 'uploader' : '/site_media/uploadify/uploadify.swf', 'script' : '{%url uploadify_script %}', 'cancelImg' : '/site_media/uploadify/cancel.png', 'folder' : '/upload', 'auto' : false,// 'multi': true,//设置可以上传多个文件 'queueSizeLimit':20,//设置可以同时20个文件 'removeCompleted':false,// 'sizeLimit':10240000,//设置上传文件大小单位kb 'fileExt':'*.jpg;*.gif;*.png',//设置上传文件类型为常用图片格式 'fileDesc':'Image Files', 'onInit': function () {}, 'onError' : function (event,ID,fileObj,errorObj) { $('#id_span_msg').html("上传失败,错误码:"+errorObj.type+" "+errorObj.info); }, 'onSelect': function (e, queueId, fileObj) { $('#id_span_msg').html(""); }, 'onAllComplete': function (event, data) { if(data.filesUploaded>=1){ $('#id_span_msg').html("上传成功!"); } } }); }); </script> <style type="text/css"> /* Apply these styles only when #preview-pane has been placed within the Jcrop widget */ .jcrop-holder #preview-pane { display: block; position: absolute; z-index: 2000; top: 10px; right: -280px; padding: 6px; border: 1px rgba(0,0,0,.4) solid; background-color: white; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2); -moz-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2); box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2); } /* The Javascript code will set the aspect ratio of the crop area based on the size of the thumbnail preview, specified here */ #preview-pane .preview-container { width: 180px; height: 180px; overflow: hidden; } </style> </head> <body> <h1>请选择图片上传</h1> <div class="demo-box"> <form action="." method="post" enctype="multipart/form-data">{% csrf_token %} <input type="file" name="Filedata"/> <input type="submit" value="上传"/> {%if message%}{{message}}{%endif%} {% ifequal upload_flag 1 %} <img id="target" src="/site_media/upload/tmp/{{savepath}}"/> <div id="preview-pane"> <div class="preview-container"> <img src="/site_media/upload/tmp/{{savepath}}" class="jcrop-preview" alt="Preview" /> </div> </div> <br/><br/> <div id="echo_href_msg" > <a href="#" onclick="replace_image('1','{{savepath}}');" >你确定要替换原来的图片?</a> </div> {%endifequal%} </form> </div> <!-- <p></p> <h1>Uploadify组件上传方式</h1> <div class="demo-box"> <input id="file_upload" type="file" name="Filedata"> <div id="file_uploadQueue" class="uploadifyQueue"></div> <p><a href="javascript:$('#file_upload').uploadifyUpload()">上传图片</a> <a href="javascript:$('#file_upload').uploadifyClearQueue()">取消上传</a> </p> <p><span id="id_span_msg"></span></p> //--> </div> </body> </html>
c)的代码:
from django.conf.urls.defaults import patterns, url urlpatterns = patterns('xue_wan_le.UploadImage.views', url(r'^$', 'index', name='uploadify'), url(r'^index/', 'index',name="uploadify_index"), url(r'^uploadify_script/', 'uploadify_script',name="uploadify_script"), )
d)的代码:
#coding=utf-8 from django.http import HttpResponse from django.template import RequestContext from django.shortcuts import render_to_response import os,ImageFile,uuid,shutil from django.conf import settings from django.views.decorators.csrf import csrf_exempt from django.utils import simplejson from xue_wan_le.Common.CookieUtil import CookieUtil def index(request): ctx=dict() uuid='' marginTop=0 marginLeft=0 width=0 height=0 if request.GET.get('marginTop'): marginTop=int(request.GET.get('marginTop')) if request.GET.get('marginLeft'): marginLeft=int(request.GET.get('marginLeft')) if request.GET.get('width'): width=int(request.GET.get('width')) if request.GET.get('height'): height=int(request.GET.get('height')) if request.GET.get('uuid'): uuid=request.GET.get('uuid') print '===uuid:'+request.GET.get('uuid') request.session['replace_site_icon_uuid'] = uuid#保存UUID if request.GET.get('replace_flag'): filepath=request.GET.get('savepath') olduuid=request.session['replace_site_icon_uuid'] print '===filepath:'+filepath print '===olduuid:'+olduuid print '====uuid:'+uuid path=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP) if filepath and olduuid: if os.path.isfile(os.path.join(path,filepath)): #先把新文件,换成旧文件名字 try: print 'olduuid:'+olduuid newname=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP,olduuid+'.jpg') print 'newname:'+newname os.rename(os.path.join(path,filepath),newname) #覆盖 shutil.move(os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP,olduuid+'.jpg'), os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg')) #裁切 try: from PIL import Image path=os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg') f = Image.open(path) xsize,ysize=f.size print 'image size:'+str(xsize)+":"+str(ysize) #box变量是一个四元组(左,上,右,下)。 box=(marginLeft,marginTop,marginLeft+width,marginTop+height) print box print '----1' f.crop(box).save(path) print '----2' print 'crop image:'+path except Exception,e: print e return HttpResponse(simplejson.dumps({'message':'替换成功,关闭窗口'})) except Exception,e: print e else: print 'error.==' if request.method=="POST": file = request.FILES.get("Filedata",None) (upload_flag,savepath)=_upload(file) if upload_flag: ctx["message"]=u"上传成功!" ctx["upload_flag"]=1 else: ctx["message"]=u"上传出错!" ctx["upload_flag"]=0 ctx["savepath"]=savepath return render_to_response("uploadpic/index.html",ctx,RequestContext(request)) @csrf_exempt def uploadify_script(request): response=HttpResponse() response['Content-Type']="text/javascript" ret="0" file = request.FILES.get("Filedata",None) if file: if _upload(file): ret="1" ret="2" response.write(ret) return response def _upload(file): '''图片上传函数''' if file: path=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP) if not os.path.exists(path): #如果目录不存在创建目录 os.makedirs(path) file_name=str(uuid.uuid1())+".jpg" path_file=os.path.join(path,file_name) parser = ImageFile.Parser() for chunk in file.chunks(): parser.feed(chunk) img = parser.close() try: if img.mode != "RGB": img = img.convert("RGB") img.save(path_file, 'jpeg',quality=100) print 'img.save:'+path_file except Exception,e: print e return (False,"") return (True,file_name) return (False,"")
index.html和view.py是功能实现的主要部分,如果有疑问可以发评论给我,或者新浪微博私信给我http://weibo.com/changeself