我想处理上传到我的
SimpleHTTPServer
的图像。
我尝试直接将
rfile
喂给Image.open()
,但这不起作用。
import SimpleHTTPServer
import SocketServer
from PIL import Image
class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_POST(self):
img = Image.open(self.rfile)
# resize, crop, etc.
httpd = SocketServer.TCPServer(("", PORT), Handler)
httpd.serve_forever()
我可以将图像保存到磁盘并使用 PIL 正常打开它,但这听起来不像是最快/最干净的方法。
self.rfile
是围绕 socket
对象的简单的类似文件的包装器(请参阅生成此文件对象的 socket.makefile()
函数)。包装器不支持查找,因为只有数据流提供给该对象,而不是磁盘上的随机访问区域。
PIL 另一方面,需要随机访问整个文件(通过查找),因为大多数图像格式使用文件中的不同部分来存储 PIL 对象在不同时间需要访问的不同信息。
您唯一的选择是将数据从
self.rfile
复制到支持查找的文件对象。我建议您为此使用 tempfile.SpooledTemporaryFile()
,因为它将数据存储在内存中,直到达到阈值,然后再将数据移动到磁盘。
您需要小心,仅将
Content-Length
标头字节复制到本地文件中;发送少于或更多的字节都是错误的。如果您不这样做,您的服务器很容易因发送超出磁盘空间处理能力的 POST 请求而崩溃。
也许使用
while
循环来复制缓冲区,直到达到Content-Length
字节,或者套接字不再返回数据:
from tempfile import SpooledTemporaryFile
def do_POST(self):
try:
length = int(self.headers.get('content-length'))
except (TypeError, ValueError):
# no Content-Length or not a number
# return error
if length > SOME_MAXIMUM_LENGTH:
# return error
with SpooledTemporaryFile() as img_file:
read = 0
while read < length:
buffer = self.rfile.read(1024)
if not buffer:
# too short, return error
img_file.write(buffer)
read += len(buffer)
if read > length or self.rfile.read(1):
# too long, return error
img_file.seek(0)
img = Image.open(img_file)
如果您使用此处理程序接受
multipart/form-data
请求,您实际上必须以不同的方式解析该特定请求类型。使用 cgi.FieldStorage()
类 来处理解析,它会为您将文件放入 TemporaryFile
对象中,直接写入磁盘:
from cgi import FieldStorage
def do_POST(self):
if self.headers.get('content-type', '').lower() == 'multipart/form-data':
fields = FieldStorage(self.rfile, self.headers, environ={'METHOD': 'POST'})
imgfile = fields['image_file'] # or whatever exact field name you expect