前台利用jcrop做头像选择预览,后台通过django利用Uploadify组件上传图最终使用PIL做图像裁切

本博客详细介绍了如何在 Django 后台系统中整合前端图片预览和裁剪功能,实现用户自定义图片切块并上传至服务器。通过使用 jQuery Uploadify 组件进行文件上传,结合 jCrop 插件实现实时预览与图片裁剪,并通过 Ajax 请求将裁剪后的参数传递至后台。此外,还实现了用户界面的交互,包括头像更换提示与确认操作。

之前一直使用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)的代码:

[html]  view plain copy print ?
  1. {% extends "kidcrate/base.html" %}  
  2. {%block contentBar%}  
  3.   
  4. <link href="/site_media/uploadify/uploadify.css" type="text/css" rel="stylesheet" />    
  5. <script type="text/javascript" src="/site_media/uploadify/jquery.uploadify.min.js"></script>  
  6.   
  7.     <script type="text/javascript" src="/site_media/js/thickbox.js"></script>    
  8.     <link rel='stylesheet' type='text/css' href='/site_media/css/thickbox.css' />  
  9.   
  10. <link href="/site_media/common/jquery_webox/jquery-webox.css" rel="stylesheet" type="text/css">  
  11.   
  12. <script src="/site_media/common/jquery_webox/jquery-webox.js"></script>  
  13. <script type="text/javascript">  
  14. $(document).ready(function(){  
  15.   
  16.       
  17.     //iframe弹出层调用  
  18.     $('#outside').click(function(){  
  19.         $.webox({  
  20.   
  21.             height:500,  
  22.             width:800,  
  23.             bgvisibel:true,  
  24.             title:'修改头像',  
  25.             iframe:'/uploadify?uuid='+$('#uuid').val()+'&rd='+Math.random()  
  26.         });  
  27.     });  
  28.       
  29. })  
  30. </script>  
  31.   
  32.   
  33. <DIV class="yyh-page grid_9">  
  34.     <DIV class=widget>  
  35.         <DIV class=widget-content>  
  36.             <!-- basic form -->  
  37.             <FORM id=basic class="yyh-form tabs-rel tabs-info on" method=post  
  38.             action="/account/userinfo/">  
  39.   
  40.             <input type="hidden"  id="uuid" name="uuid" value="{{uuid}}" />  
  41.               
  42.                 <DL class=required>  
  43.                     <DT>  
  44.                         <LABEL for=producer>  
  45.                             账号  
  46.                         </LABEL>  
  47.                     </DT>  
  48.                     <DD>  
  49.                         <INPUT class=inputText style="color: #400080;font - size: 16px;" readonly="true" value="{{username}}">  
  50.                         <SPAN>  
  51.                             <B class=error>  
  52.                             </B>  
  53.                         </SPAN>  
  54.                         <P class=hint>  
  55.                         </P>  
  56.                     </DD>  
  57.                     <DT>  
  58.                         <LABEL for=producer>  
  59.                             联系人真实姓名  
  60.                         </LABEL>  
  61.                     </DT>  
  62.                     <DD>  
  63.                         <INPUT class=inputText name=devName value="{{devName}}">  
  64.                         <SPAN>  
  65.                             <B class=error>  
  66.                             </B>  
  67.                         </SPAN>  
  68.                         <P class=hint>  
  69.                         </P>  
  70.                     </DD>  
  71.                 </DL>  
  72.                 <DL class=required>  
  73.                     <DT>  
  74.                         <LABEL for=phone>  
  75.                             联系电话  
  76.                         </LABEL>  
  77.                     </DT>  
  78.                     <DD>  
  79.                         <INPUT class=inputText name=contactPhone value="{{contactPhone}}">  
  80.                         <SPAN>  
  81.                             <B class=error>  
  82.                             </B>  
  83.                         </SPAN>  
  84.                         <P class=hint>  
  85.                         </P>  
  86.                     </DD>  
  87.                 </DL>  
  88.                 <DL class=required>  
  89.                     <DT>  
  90.                         <LABEL for=address>  
  91.                             联系地址  
  92.                         </LABEL>  
  93.                     </DT>  
  94.                     <DD>  
  95.                         <INPUT class=inputText name=contactAddress value="{{contactAddress}}">  
  96.                         <SPAN>  
  97.                             <B class=error>  
  98.                             </B>  
  99.                         </SPAN>  
  100.                         <P class=hint>  
  101.                             请填写真实的联系地址  
  102.                         </P>  
  103.                     </DD>  
  104.                 </DL>  
  105.                 <DL class=required>  
  106.                     <DT>  
  107.                         <LABEL for=zipcode>  
  108.                             邮政编码  
  109.                         </LABEL>  
  110.                     </DT>  
  111.                     <DD>  
  112.                         <INPUT class="inputText NumberValidate" name=contactZipCode value="{{contactZipCode}}">  
  113.                         <SPAN>  
  114.                             <B class=error>  
  115.                             </B>  
  116.                         </SPAN>  
  117.                     </DD>  
  118.                 </DL>  
  119.   
  120.                 <DL>  
  121.                     <DT>  
  122.                         <LABEL for=headimg>  
  123.                             头像  
  124.                         </LABEL>  
  125.                     </DT>  
  126.                     <DD>  
  127.   
  128.                         <img id="screenshot_img" name="screeshot_img" src="/site_media/images/account_head/femail.jpg" width="120" height="120" /><br><br/>  
  129.   
  130.   
  131.                         <div id="upload_div" style="display:visible;">  
  132.                             <a class="Button blueButton Button18" href="javascript:void(0);" id="outside"><strong>点我修改</strong></a>  
  133.                         </div>  
  134.                        
  135.   
  136.   
  137.                     </DD>  
  138.                 </DL>  
  139.   
  140.   
  141.                 <DL class=submit>  
  142.                     <DT>  
  143.                     </DT>  
  144.                     <DD>  
  145.                         <INPUT class="inputSubmit blue" value=保存 type=button onclick="this.form.submit();  
  146.                        ">  
  147.                     </DD>  
  148.                 </DL>  
  149.             </FORM>  
  150.             <!-- end of basic form -->  
  151.   
  152.         </DIV>  
  153.     </DIV>  
  154. </DIV>  
  155. {%endblock%}"  




b)的代码:

[html]  view plain copy print ?
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4. <meta charset="UTF-8">  
  5. <title>Django下利用Uploadify组件上传图片</title>  
  6.   
  7.  <link rel="stylesheet" href="/site_media/jcrop/demos/demo_files/main.css" type="text/css" />  
  8. <link rel="stylesheet" href="/site_media/jcrop/demos/demo_files/demos.css" type="text/css" />  
  9. <link rel="stylesheet" href="/site_media/jcrop/css/jquery.Jcrop.css" type="text/css" />  
  10.   
  11.   
  12. <script src="/site_media/jcrop/js/jquery.min.js"></script>  
  13. <script src="/site_media/jcrop/js/jquery.Jcrop.js"></script>  
  14. <script type="text/javascript">  
  15.     var img_top_margin,img_left_margin,img_width,img_height;//最后使用的2个变量  
  16.   
  17.   jQuery(function($){  
  18.   
  19.     // Create variables (in this scope) to hold the API and image size  
  20.     var jcrop_api,  
  21.         boundx,  
  22.         boundy,  
  23.         topw,  
  24.         leftw,  
  25.   
  26.         // Grab some information about the preview pane  
  27.         $preview = $('#preview-pane'),  
  28.         $pcnt = $('#preview-pane .preview-container'),  
  29.         $pimg = $('#preview-pane .preview-container img'),  
  30.   
  31.         xsize = $pcnt.width(),  
  32.         ysize = $pcnt.height();  
  33.       
  34.     console.log('init',[xsize,ysize]);  
  35.     $('#target').Jcrop({  
  36.       onChange: updatePreview,  
  37.       onSelect: updatePreview,  
  38.       aspectRatio: xsize / ysize  
  39.     },function(){  
  40.       // Use the API to get the real image size  
  41.       var bounds = this.getBounds();  
  42.       boundx = bounds[0];  
  43.       boundy = bounds[1];  
  44.       // Store the API in the jcrop_api variable  
  45.       jcrop_api = this;  
  46.   
  47.       // Move the preview into the jcrop container for css positioning  
  48.       $preview.appendTo(jcrop_api.ui.holder);  
  49.     });  
  50.   
  51.     function updatePreview(c)  
  52.     {  
  53.       if (parseInt(c.w) > 0)  
  54.       {  
  55.         var rx = xsize / c.w;  
  56.         var ry = ysize / c.h;  
  57.         console.log("new width:"+Math.round(rx * boundx) );  
  58.         console.log("new height:"+Math.round(ry * boundy) );  
  59.   
  60.         console.log("marginTop:"+Math.round(ry * c.y));  
  61.         console.log("marginLeft:"+Math.round(rx * c.x));  
  62.           
  63.    
  64.         img_top_margin=c.y;  
  65.         img_left_margin=c.x;  
  66.         img_width=c.w;  
  67.         img_height=c.h;  
  68.   
  69.   
  70.   
  71.         $pimg.css({  
  72.           width: Math.round(rx * boundx) + 'px',  
  73.           height: Math.round(ry * boundy) + 'px',  
  74.           marginLeft: '-' + Math.round(rx * c.x) + 'px',  
  75.           marginTop: '-' + Math.round(ry * c.y) + 'px'  
  76.         });  
  77.       }  
  78.     };  
  79.   
  80.   });  
  81.   
  82.   
  83. </script>  
  84.   
  85.   
  86.   
  87.   
  88.   
  89. <link href="/site_media/uploadify/uploadify.css" type="text/css" rel="stylesheet" />  
  90. <script type="text/javascript" src="/site_media/uploadify/swfobject.js"></script>  
  91. <script type="text/javascript" src="/site_media/uploadify/jquery.uploadify.v2.1.4.min.js"></script>  
  92.   
  93.   
  94.   
  95. <script  type="text/javascript" charset="utf-8" async defer>  
  96.      function go() {  
  97.         alert(2);  
  98.     }  
  99.   
  100.     function replace_image(flag,path,uuid) {  
  101.         console.log("==========replace_image=======");  
  102.   
  103.         console.log("marginTop:"+img_top_margin);  
  104.         console.log("marginLeft:"+img_left_margin);  
  105.   
  106.         console.log("width:"+img_width);  
  107.         console.log("height:"+img_height);  
  108.         var params="&marginTop="+img_top_margin+"&marginLeft="+img_left_margin+"&width="+img_width+"&height="+img_height;  
  109.         $.ajax({  
  110.             type: "GET",  
  111.             url: "?replace_flag="+flag+"&savepath="+path+params,  
  112.             dataType: "json",  
  113.             success: function (json) {  
  114.                 alert(json.message);  
  115.                 //document.getElementById("btnquery").click();  
  116.                  document.getElementById("echo_href_msg").innerHTML = json.message;  
  117.             }  
  118.         })  
  119.     }  
  120. </script>  
  121.   
  122.   
  123.   
  124. <script type="text/javascript">  
  125. $(document).ready(function() {  
  126.   
  127.   $('#file_upload').uploadify({  
  128.     'uploader'  : '/site_media/uploadify/uploadify.swf',  
  129.     'script'    : '{%url uploadify_script %}',  
  130.     'cancelImg' : '/site_media/uploadify/cancel.png',  
  131.     'folder'    : '/upload',  
  132.     'auto'      : false,//  
  133.     'multi': true,//设置可以上传多个文件  
  134.     'queueSizeLimit':20,//设置可以同时20个文件  
  135.     'removeCompleted':false,//  
  136.     'sizeLimit':10240000,//设置上传文件大小单位kb  
  137.     'fileExt':'*.jpg;*.gif;*.png',//设置上传文件类型为常用图片格式  
  138.     'fileDesc':'Image Files',                  
  139.     'onInit': function () {},  
  140.     'onError' : function (event,ID,fileObj,errorObj) {  
  141.             $('#id_span_msg').html("上传失败,错误码:"+errorObj.type+" "+errorObj.info);  
  142.         },  
  143.     'onSelect': function (e, queueId, fileObj) {  
  144.         $('#id_span_msg').html("");  
  145.     },  
  146.     'onAllComplete': function (event, data) {  
  147.         if(data.filesUploaded>=1){  
  148.           $('#id_span_msg').html("上传成功!");  
  149.         }                      
  150.     }                  
  151.   });  
  152.   
  153.   
  154.   
  155.   
  156.   
  157. });  
  158. </script>  
  159.   
  160.   
  161. <style type="text/css">  
  162.   
  163. /* Apply these styles only when #preview-pane has  
  164.    been placed within the Jcrop widget */  
  165. .jcrop-holder #preview-pane {  
  166.   display: block;  
  167.   position: absolute;  
  168.   z-index: 2000;  
  169.   top: 10px;  
  170.   right: -280px;  
  171.   padding: 6px;  
  172.   border: 1px rgba(0,0,0,.4) solid;  
  173.   background-color: white;  
  174.   
  175.   -webkit-border-radius: 6px;  
  176.   -moz-border-radius: 6px;  
  177.   border-radius: 6px;  
  178.   
  179.   -webkit-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2);  
  180.   -moz-box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2);  
  181.   box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.2);  
  182. }  
  183.   
  184. /* The Javascript code will set the aspect ratio of the crop  
  185.    area based on the size of the thumbnail preview,  
  186.    specified here */  
  187. #preview-pane .preview-container {  
  188.   width: 180px;  
  189.   height: 180px;  
  190.   overflow: hidden;  
  191. }  
  192.   
  193. </style>  
  194.   
  195. </head>  
  196. <body>  
  197.   <h1>请选择图片上传</h1>  
  198. <div class="demo-box">  
  199. <form action="." method="post" enctype="multipart/form-data">{% csrf_token %}  
  200.   
  201. <input type="file" name="Filedata"/>  
  202.   <input type="submit" value="上传"/> {%if message%}{{message}}{%endif%}  
  203.   {% ifequal  upload_flag 1 %}  
  204.   <img id="target" src="/site_media/upload/tmp/{{savepath}}"/>  
  205.   
  206.   <div id="preview-pane">  
  207.     <div class="preview-container">  
  208.       <img src="/site_media/upload/tmp/{{savepath}}" class="jcrop-preview" alt="Preview" />  
  209.     </div>  
  210.   </div>  
  211.   
  212.   
  213.   <br/><br/>  
  214.   <div id="echo_href_msg" >  
  215.     <a href="#" onclick="replace_image('1','{{savepath}}');" >你确定要替换原来的图片?</a>  
  216. </div>  
  217.     {%endifequal%}  
  218. </form>  
  219. </div>  
  220. <!--  
  221. <p></p>  
  222. <h1>Uploadify组件上传方式</h1>  
  223. <div class="demo-box">  
  224.     <input id="file_upload" type="file" name="Filedata">  
  225.     <div id="file_uploadQueue" class="uploadifyQueue"></div>  
  226.     <p><a href="javascript:$('#file_upload').uploadifyUpload()">上传图片</a>  
  227.     <a href="javascript:$('#file_upload').uploadifyClearQueue()">取消上传</a>  
  228.     </p>  
  229.     <p><span id="id_span_msg"></span></p>  
  230.     //-->  
  231. </div>  
  232.   
  233. </body>  
  234. </html>  

c)的代码:

[python]  view plain copy print ?
  1. from django.conf.urls.defaults import patterns, url  
  2.   
  3. urlpatterns = patterns('xue_wan_le.UploadImage.views',  
  4.     url(r'^$''index', name='uploadify'),  
  5.     url(r'^index/''index',name="uploadify_index"),  
  6.     url(r'^uploadify_script/''uploadify_script',name="uploadify_script"),  
  7. )  

d)的代码:

[python]  view plain copy print ?
  1. #coding=utf-8  
  2. from django.http import HttpResponse  
  3. from django.template import RequestContext  
  4. from django.shortcuts import render_to_response  
  5. import os,ImageFile,uuid,shutil  
  6. from django.conf import settings  
  7. from django.views.decorators.csrf import csrf_exempt  
  8. from django.utils import simplejson  
  9.   
  10. from xue_wan_le.Common.CookieUtil import CookieUtil   
  11.   
  12. def index(request):  
  13.     ctx=dict()  
  14.   
  15.     uuid=''  
  16.     marginTop=0  
  17.     marginLeft=0  
  18.     width=0  
  19.     height=0  
  20.     if request.GET.get('marginTop'):  
  21.         marginTop=int(request.GET.get('marginTop'))  
  22.     if request.GET.get('marginLeft'):  
  23.         marginLeft=int(request.GET.get('marginLeft'))  
  24.     if request.GET.get('width'):  
  25.         width=int(request.GET.get('width'))  
  26.     if request.GET.get('height'):  
  27.         height=int(request.GET.get('height'))  
  28.     if request.GET.get('uuid'):  
  29.         uuid=request.GET.get('uuid')  
  30.         print '===uuid:'+request.GET.get('uuid')  
  31.         request.session['replace_site_icon_uuid'] = uuid#保存UUID  
  32.           
  33.     if request.GET.get('replace_flag'):  
  34.         filepath=request.GET.get('savepath')  
  35.         olduuid=request.session['replace_site_icon_uuid']  
  36.   
  37.         print '===filepath:'+filepath  
  38.         print '===olduuid:'+olduuid  
  39.         print '====uuid:'+uuid  
  40.         path=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP)  
  41.         if filepath and olduuid:  
  42.             if os.path.isfile(os.path.join(path,filepath)):  
  43.                 #先把新文件,换成旧文件名字  
  44.                 try:  
  45.                     print 'olduuid:'+olduuid  
  46.                     newname=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP,olduuid+'.jpg')  
  47.                     print 'newname:'+newname  
  48.   
  49.                       
  50.                     os.rename(os.path.join(path,filepath),newname)  
  51.                     #覆盖  
  52.                     shutil.move(os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP,olduuid+'.jpg'),  
  53.                                 os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg'))  
  54.   
  55.                     #裁切  
  56.                     try:  
  57.                         from PIL import Image  
  58.                         path=os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg')  
  59.                         f = Image.open(path)  
  60.                         xsize,ysize=f.size  
  61.                         print 'image size:'+str(xsize)+":"+str(ysize)  
  62.                         #box变量是一个四元组(左,上,右,下)。    
  63.                         box=(marginLeft,marginTop,marginLeft+width,marginTop+height)  
  64.                         print box  
  65.                         print '----1'  
  66.                         f.crop(box).save(path)  
  67.                         print '----2'  
  68.                         print 'crop image:'+path  
  69.                     except Exception,e:  
  70.                         print e  
  71.                     return HttpResponse(simplejson.dumps({'message':'替换成功,关闭窗口'}))  
  72.                 except Exception,e:  
  73.                     print e  
  74.             else:  
  75.                 print 'error.=='  
  76.                   
  77.       
  78.     if request.method=="POST":  
  79.         file = request.FILES.get("Filedata",None)  
  80.         (upload_flag,savepath)=_upload(file)  
  81.         if upload_flag:  
  82.             ctx["message"]=u"上传成功!"  
  83.             ctx["upload_flag"]=1  
  84.         else:  
  85.             ctx["message"]=u"上传出错!"  
  86.             ctx["upload_flag"]=0  
  87.         ctx["savepath"]=savepath  
  88.           
  89.     return render_to_response("uploadpic/index.html",ctx,RequestContext(request))  
  90.  
  91. @csrf_exempt  
  92. def uploadify_script(request):  
  93.     response=HttpResponse()  
  94.     response['Content-Type']="text/javascript"  
  95.     ret="0"          
  96.     file = request.FILES.get("Filedata",None)          
  97.     if file:              
  98.         if _upload(file):  
  99.             ret="1"  
  100.         ret="2"  
  101.     response.write(ret)  
  102.     return response  
  103.   
  104. def _upload(file):  
  105.     '''''图片上传函数'''  
  106.     if file:              
  107.         path=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP)  
  108.         if not os.path.exists(path): #如果目录不存在创建目录  
  109.             os.makedirs(path)  
  110.               
  111.         file_name=str(uuid.uuid1())+".jpg"        
  112.         path_file=os.path.join(path,file_name)  
  113.         parser = ImageFile.Parser()    
  114.         for chunk in file.chunks():    
  115.             parser.feed(chunk)    
  116.         img = parser.close()  
  117.         try:  
  118.             if img.mode != "RGB":  
  119.                 img = img.convert("RGB")  
  120.             img.save(path_file, 'jpeg',quality=100)  
  121.             print 'img.save:'+path_file  
  122.         except Exception,e:  
  123.             print e  
  124.             return (False,"")  
  125.         return (True,file_name)  
  126.     return (False,"")  


index.html和view.py是功能实现的主要部分,如果有疑问可以发评论给我,或者新浪微博私信给我http://weibo.com/changeself

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值