我想处理上传到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正常打开,但它听起来不是最快/最干净的方式。
答案 0 :(得分:3)
self.rfile
是socket
对象周围的简单文件包装器(请参阅生成此文件对象的socket.makefile()
function)。包装器不支持搜索,因为只有一个数据流提供此对象,而不是磁盘上的随机访问区域。
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 imgfile:
read = 0
while read < length:
buffer = self.rfile.read(1024)
if not buffer:
# too short, return error
imgfile.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()
class来处理解析,它会将文件直接放入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