PIL:从 POST HTTP 请求打开图像

问题描述 投票:0回答:1

我想处理上传到我的

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 正常打开它,但这听起来不像是最快/最干净的方法。

python http post python-imaging-library
1个回答
3
投票

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
© www.soinside.com 2019 - 2024. All rights reserved.