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

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

b)的代码:

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

c)的代码:

  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)的代码:

  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. os.rename(os.path.join(path,filepath),newname)
  50. #覆盖
  51. shutil.move(os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP,olduuid+'.jpg'),
  52. os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg'))
  53.  
  54. #裁切
  55. try:
  56. from PIL import Image
  57. path=os.path.join(settings.MEDIA_ROOT,'urls','sitethumbs',olduuid+'.jpg')
  58. f = Image.open(path)
  59. xsize,ysize=f.size
  60. print 'image size:'+str(xsize)+":"+str(ysize)
  61. #box变量是一个四元组(左,上,右,下)。
  62. box=(marginLeft,marginTop,marginLeft+width,marginTop+height)
  63. print box
  64. print '----1'
  65. f.crop(box).save(path)
  66. print '----2'
  67. print 'crop image:'+path
  68. except Exception,e:
  69. print e
  70. return HttpResponse(simplejson.dumps({'message':'替换成功,关闭窗口'}))
  71. except Exception,e:
  72. print e
  73. else:
  74. print 'error.=='
  75.  
  76. if request.method=="POST":
  77. file = request.FILES.get("Filedata",None)
  78. (upload_flag,savepath)=_upload(file)
  79. if upload_flag:
  80. ctx["message"]=u"上传成功!"
  81. ctx["upload_flag"]=1
  82. else:
  83. ctx["message"]=u"上传出错!"
  84. ctx["upload_flag"]=0
  85. ctx["savepath"]=savepath
  86.  
  87. return render_to_response("uploadpic/index.html",ctx,RequestContext(request))
  88.  
  89. @csrf_exempt
  90. def uploadify_script(request):
  91. response=HttpResponse()
  92. response['Content-Type']="text/javascript"
  93. ret="0"
  94. file = request.FILES.get("Filedata",None)
  95. if file:
  96. if _upload(file):
  97. ret="1"
  98. ret="2"
  99. response.write(ret)
  100. return response
  101.  
  102. def _upload(file):
  103. '''图片上传函数'''
  104. if file:
  105. path=os.path.join(settings.MEDIA_ROOT,settings.SOURCE_IMAGE_TMP)
  106. if not os.path.exists(path): #如果目录不存在创建目录
  107. os.makedirs(path)
  108.  
  109. file_name=str(uuid.uuid1())+".jpg"
  110. path_file=os.path.join(path,file_name)
  111. parser = ImageFile.Parser()
  112. for chunk in file.chunks():
  113. parser.feed(chunk)
  114. img = parser.close()
  115. try:
  116. if img.mode != "RGB":
  117. img = img.convert("RGB")
  118. img.save(path_file, 'jpeg',quality=100)
  119. print 'img.save:'+path_file
  120. except Exception,e:
  121. print e
  122. return (False,"")
  123. return (True,file_name)
  124. return (False,"")

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

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

  1. [App Store Connect帮助]四、添加 App 图标、App 预览和屏幕快照(3)上传 App 预览和屏幕快照

    请上传至多三个 App 预览和至多十张屏幕快照.如果您的 App 在不同设备尺寸和本地化内容间都相同,仅提供所要求的最高分辨率的屏幕快照即可. 对于 iPhone,必须提供用于 5.5 英寸设备(iP ...

  2. 利用tornado实现表格文件预览

    项目介绍   本文将介绍笔者的一个项目,主要是利用tornado实现表格文件的预览,能够浏览的表格文件支持CSV以及Excel文件.预览的界面如下:   下面我们将看到这个功能是如何通过tornado ...

  3. 如何利用Python实现Office在线预览

    目前,市场对于Office在线预览功能的需求是很大的.对于我们用户本身来说,下载Office文件后再实现预览是极其不方便的,何况还有一些不能打开的专业文档.压缩文件等.此时,能提供在线预览服务的软件就 ...

  4. javascript 利用FileReader和滤镜上传图片预览

    FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File或 Blob对象指定要读取的文件或数据. 1.FileReader接口的方法 Fi ...

  5. 纯javascript代码实现浏览器图片选择预览、旋转、批量上传

    工作中遇到的业务场景,和同事一起研究了下,主要是为了兼容IE版本 其实就是一些琐碎的知识点在网上搜集下解决方式,然后集成了下,主要有以下点: 1. IE input type=file的图片预览要用I ...

  6. DAY19-上传头像并预览

    一个简单的注册页面: <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  7. 表单提交学习笔记(三)—利用Request.Files上传图片并预览

    一.html页面如下 <div id="container"> <form id="myForm"> <p class=" ...

  8. django 上传头像并预览 3选1

    注册页面的头像上传 register.html<!DOCTYPE html> <html lang="en"> <head> <meta ...

  9. MUI 单个图片上传预览(拍照+系统相册):先选择->预览->上传提交

    1 html部分 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> < ...

随机推荐

  1. leetcode_question_114 Flatten Binary Tree to Linked List

    Given a binary tree, flatten it to a linked list in-place. For example, Given 1 / \ 2 5 / \ \ 3 4 6 ...

  2. Lucene.net常见功能实现知识汇总

    在开发SearchEasy Site SearchEngine(搜易站内搜索引擎)的时候,经常会遇到一些搜索引擎的常见功能如何实现的问题,比如实现相关度百分比显示?如何实现在结果中搜索等等诸如此类常见 ...

  3. SEO的URL如何优化才是最佳

    原文地址:http://www.chinaz.com/web/2007/0413/6841.shtml 很多人都知道URL对SEO的重要之处,但是很多站点却忽略了站点的路径优化.今天本人在这里写几点关 ...

  4. UGUI 帧动画插件

    最近在开发一款功夫猫游戏,本来使用Unity Sprite制作,但是发现Sprite对各种分辨率不支持. 看着游戏很简单就使用UGUI制作,在中途发现有很多帧动画播放,使用了Animation调整使用 ...

  5. mycat实例(3)

    配置MyCat 4. 配置schema.xml schema.xml里面管理着MyCat的逻辑库.表,每张表使用的分片规则.分布在哪个DataNode以及DataSource上. 之前的例子: < ...

  6. POJ 3254 炮兵阵地(状态压缩DP)

    题意:由方格组成的矩阵,每个方格可以放大炮用P表示,不可以放大炮用H表示,求放最多的大炮,大炮与大炮间不会互相攻击.大炮的攻击范围为两个方格. 分析:这次当前行的状态不仅和上一行有关,还和上上行有关, ...

  7. 第04章-VTK基础(4)

    [译者:这个系列教程是以Kitware公司出版的<VTK User's Guide -11th edition>一书作的中文翻译(出版时间2010年,ISBN: 978-1-930934- ...

  8. [置顶] 正则表达式应用:匹配IP地址

    都知道iP地址有四个数值,三个点号组成.三个数值的具体范围为0到255,为了使用正则表达式匹配就必须分析IP地址的组成 1先分析数值,2再组合数值和点号 1先分析数值 IP地址的数字范围从0到255, ...

  9. [跟我学spring学习笔记][DI循环依赖]

    循环依赖 什么是循环依赖? 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方. Spring容器循环依赖包括构造器循环依赖和setter循环依赖,那Spring容器如何解决循环依赖呢? ...

  10. ToString()和Convert.ToString()的区别

    ToString()和Convert.ToString()的区别 一般情况下,这两种方法都可以通用,但是当返回的数据类型中有可能出现null值时如果调用ToString方法了,就会返回NullRefe ...