HTTP自己是个无状态的协议,就是说,每个到达服务器的请求都独立于以前的请求。若是须要特定的状态,则须要把它加到应用层上css
Django这样的框架使用Cookie及其余机制将统一客户端发送的请求绑定在一块儿。html
借助在Django学习1中的模板,建立一个placeholder的新项目python
django-admin startproject placeholder --template=project_name
视图与URL模式web
咱们用两个视图来生成响应,一个视图用来按照请求的宽度高度渲染占位图像,另外一个用来渲染主页面内容。正则表达式
def placeholder(request,width,height): # TODO : Rest of the view will go here return HttpResponse('OK') def index(request): return HttpResponse('Hello World')
咱们还要一个指向placeholder视图的路由,包含两个参数:width和height,Django的URL模式使用正则表达式来匹配输入的URLdjango
# 关联URL模式,捕捉的模式组会以位置参数的形式传递给视图, # 命名的组会以关键字参数的形式传递,经过?P语法来捕获被命名的组, # 并用[0-9]来匹配任意数字字符 urlpatterns = ( url(r'^image/(?P<width>[0-9]+)×(?P<height>[0-9]+)/',placeholder,name='placeholder'), url(r'^$',index,name='homepage'), )
完成模式的设置后,像**/image/30×25/的这样的URL会被路由到placeholder中,并传递这些数值(width=30、height=50)。同时以homepage为名称与placeholder视图新的路由一块儿被加入index**路由上。浏览器
咱们说到占位视图接收两个整数参数来设置图片的高度和宽度。可是尽管正则表达式保证了高度宽度只包含整数,但它们还是以字符串的形式传递给视图的。为了确保在一个可管理的尺寸内,咱们用表单来实现验证。缓存
典型的表单用于验证POST和GET内容,它们也能够用于验证URL和cookie的特定值。服务器
from django.http import HttpResponse,HttpResponseBadRequest from django.conf.urls import url from django.core.wsgi import get_wsgi_application from django import forms # 视图 class ImageForm(forms.Form): height = forms.IntegerField(min_value=1, max_value=2000) width = forms.IntegerField(min_value=1, max_value=2000) def placeholder(request,width,height): form = ImageForm({'height':height,'width':width}) if form.is_valid(): height = form.cleaned_data['height'] width = form.cleaned_data['width'] # TODO : Generate image of requested size return HttpResponse('OK') else: # 若是表单无效,视图发送错误信息返回客户端 return HttpResponseBadRequest('Invalid Image Request')
要生成实际的图片,咱们还须要安装Pillow模块来处理图片:cookie
pip install Pillow
经过Pillow建立图片须要两个参数,一个以元组表示的颜色模式和尺寸。
咱们在placeholder.py中的视图使用RGB模式和在表单中处理过的数据尺寸
from io import BytesIO from PIL import Image class ImageForm(forms.Form): height = forms.IntegerField(min_value=1, max_value=2000) width = forms.IntegerField(min_value=1, max_value=2000) def generate(self,image_format='PNG'): height = self.cleaned_data['height'] width = self.cleaned_data['width'] image = Image.new('RGB',(width,height)) content = BytesIO() image.save(content,image_format) content.seek(0) return content def placeholder(request,width,height): form = ImageForm({'height':height,'width':width}) if form.is_valid(): # 视图调用from.generate 来获取建立的图片,图片的字节被用于以后建立响应体 image = form.generate() return HttpResponse(image,content_type='image/png') else: return HttpResponseBadRequest('Invalid Image Request')
Image 中加入了一个新的generate方法,用于封装建立图片的逻辑,它接收一个参数来设置图片格式,默认为PNG,并以字节的形式返回图片内容。
表单验证图片尺寸经过后,视图会成功返回一张请求宽度和高度的PNG图片。图片内容不写入磁盘,直接发送给客户端。
咱们还可使用ImageDraw模块在图片中加入文字,如:
from PIL import Image,ImageDraw def generate(self,image_format='PNG'): height = self.cleaned_data['height'] width = self.cleaned_data['width'] image = Image.new('RGB',(width,height)) # 使用ImageDrawm模块在图片中加入文字 draw = ImageDraw.Draw(image) text = '{}×{}'.format(width,height) textwidth,textheight = draw.textsize(text) if textwidth < width and textheight < height : # // 是整数除法 texttop = (height - textheight) // 2 textleft = (width - textwidth) // 2 # 向尺寸合适的地方加入覆盖文字 draw.text((textleft,texttop),text,fill=(255,255,255)) content = BytesIO() image.save(content,image_format) content.seek(0) return content
每次请求视图时,占位图片视图都会从新生成图片。因为图片的宽度和高度是由最初值来设置的,经常会对服务器提出没必要要的请求。
缓存是避免这种重复的重要办法,当要肯定如何为服务提供缓存时,有两种方案可供考虑:服务器端和客户端。
对于服务器端缓存,能够方便地使用Django地缓存工具。这会把内存开销转换为缓存存储,同时会节约生成图片所需的CPU周期。
``` python
from django.core.cache import cache def generate(self,image_format='PNG'): height = self.cleaned_data['height'] width = self.cleaned_data['width'] # 经过宽度、高度和图片格式生成一个缓存键值 key = '{}.{}.{}'.format(width,height,image_format) # 新图片建立前,检查缓存是否已经存储了图片 content = cache.get(key) if content is None: image = Image.new('RGB',(width,height)) # 使用ImageDrawm模块在图片中加入文字 draw = ImageDraw.Draw(image) text = '{}×{}'.format(width,height) textwidth,textheight = draw.textsize(text) if textwidth < width and textheight < height : # // 是整数除法 texttop = (height - textheight) // 2 textleft = (width - textwidth) // 2 draw.text((textleft,texttop),text,fill=(255,255,255)) content = BytesIO() image.save(content,image_format) content.seek(0) # 当未找到缓存图片且建立了新图片时,经过键值将图片保存一小时 cache.set(key,content,60 * 60) return content ```
缓存的另外一个方案,就是关注客户端性能并使用浏览器內建的缓存。Django引入了了一个建立并使用视图的ETag标头的etag修饰符。该修饰符接收一个参数,一个从请求和视图参数中生成ETag标头的函数。
import os import hashlib from django.views.decorators.http import etag # generate_etag是个新函数,接收placeholder视图中的参数 # 使用hashlib来返回一个基于width和height值变化的不透明的ETag值 def generate_etag(request,width,height): content = 'Placeholder:{0}×{1}'.format(width,height) return hashlib.sha1(content.encode('utf-8')).hexdigest() # generate_etag函数会被发送到placeholder视图的etag修饰符中 @etag(generate_etag) def placeholder(request,width,height):
使用etag修饰符具备在视图被访问以前进行ETag计算的优点。
完成占位视图后,须要渲染一个基本的HTML模板。首先咱们加入Static和Template设置。让Django能够按照路径找到模板和静态资源。添加资源后的目录应该是这样的:
placeholder/ placeholder.py templates/ home.html static/ site.css
手动配置路径时,为了不对路径的硬性编码,使用Python标准库中的os模块
import os import hashlib import sys # 附加配置 DEBUG = os.environ.get('DEBUG','on') == 'on' # 随机的秘钥 SECRET_KEY = os.environ.get('SECRET_KEY','p*0c^!*uu70+vqq%y@xp66g*n8i9$au9$nx+d4p))6d_&kl@nb') BASE_DIR = os.path.dirname(__file__) # 设置 settings.configure( DEBUG = DEBUG, SECRET_KEY = SECRET_KEY, ROOT_URLCONF = __name__, MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ), INSTALLED_APPS=( 'django.contrib.staticfiles', ), TEMPLATES=( { 'BACKEND':'django.template.backends.django.DjangoTemplates', 'DIRS':(os.path.join(BASE_DIR,'templates'),), }, ), STATICFILES_DIRS=( os.path.join(BASE_DIR,'static'), ), STATIC_URL = '/static/', )
最后,咱们加入简单的主页面模板和CSS文件,
** home.html**:
{% load staticfiles %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Django Placeholder Images</title> <link rel="stylesheet" href="{% static 'site.css' %}" type="text/css"> </head> <body> <h1>Django Placeholder Images</h1> <p>This server can be used for serving placeholder images for any web page.</p> <p>To request a placeholder image of a given width and height simply include an image with the source pointing to <b>/image/<width>x<height>/</b> on this server such as:</p> <pre> <img src="{{ example }}" > </pre> <h2>Examples</h2> <ul> <li><img src="{% url 'placeholder' width=50 height=50 %}"></li> <li><img src="{% url 'placeholder' width=100 height=50 %}"></li> <li><img src="{% url 'placeholder' width=50 height=100 %}"></li> </ul> </body> </html>
site.css:
body{ text-align: center; } ul{ list-type:none; } li{ display: inline-block; }
固然,咱们要在placeholder.py中更新index视图来渲染这个模板。
from django.core.urlresolvers import reverse from django.shortcuts import render #更新index视图来渲染模板 def index(request): # 更新过的index视图经过翻转placeholder视图来建立一个URL样例 # 并将它传给模板上下文 example = reverse('placeholder',kwargs={'width':50,'height':50}) context = { 'example':request.build_absolute_uri(example) } # 经过render快捷方式来渲染home.html模板 return render(request,'home.html',context)
运行
最后运行完整的项目查看建立的占位图片服务
python placeholder.py runserver
结果:
应用在python3环境下顺利跑通,在python2.7下会遇到字符编码问题