欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第十二章:互联网-urllib.request:网络资源访问--上传文件

程序员文章站 2022-07-14 08:45:17
...

12.2.6 上传文件
要对文件编码以完成上传,与使用简单表单相比,这需要多做一些工作。要在请求体中构造已给完整的MIME消息,使得服务器可以把收到的表单域与上传的文件区分开。

开启POST服务器。

import io
import mimetypes
from urllib import request
import uuid

class MultiPartForm:
    """Accumulate the data to be used  when posting a form."""

    def __init__(self):
        self.form_fields = []
        self.files = []
        # Use a large random byte string to separate
        # parts of the MIME data.
        self.boundary = uuid.uuid4().hex.encode('utf-8')

    def get_content_type(self):
        return 'multipart/form-data;boundary={}'.format(
            self.boundary.decode('utf-8'))

    def add_field(self,name,value):
        """Add a simple field to the form data."""
        self.form_fields.append((name,value))

    def add_file(self,fieldname,filename,fileHandle,mimetype=None):
        """Add a file to be uploaded."""
        body = fileHandle.read()
        if mimetype is None:
            mimetype = (
            mimetypes.guess_type(filename)[0] or
            'application/octet-stream'
            )
        self.files.append((fieldname,filename,mimetype,body))
        return

    @staticmethod
    def _form_data(name):
        return ('Content-Disposition: form-data; '
                'name="{}"\r\n').format(name).encode('utf-8')

    @staticmethod
    def _attached_file(name,filename):
        return ('Content-Disposition:file; '
                'name="{}";filename="{}"\r\n').format(
                    name,filename).encode('utf-8')

    @staticmethod
    def _content_type(ct):
        return 'Content-Type: {}\r\n'.format(ct).encode('utf-8')

    def __bytes__(self):
        """Return a byte-string representing the form data,
        including attached files.
        """
        buffer = io.BytesIO()
        boundary = b'--' + self.boundary + b'\r\n'

        # Add the form fields.
        for name,value in self.form_fields:
            buffer.write(boundary)
            buffer.write(self._form_data(name))
            buffer.write(b'\r\n')
            buffer.write(value.encode('utf-8'))
            buffer.write(b'\r\n')

        # Add the files to upload.
        for f_name,filename,f_content_type,body in self.files:
            buffer.write(boundary)
            buffer.write(self._attached_file(f_name,filename))
            buffer.write(self._content_type(f_content_type))
            buffer.write(b'\r\n')
            buffer.write(body)
            buffer.write(b'\r\n')

        buffer.write(b'--' + self.boundary + b'--\r\n')
        return buffer.getvalue()

if __name__ == '__main__':
    # Create the form with simple fields.
    form = MultiPartForm()
    form.add_field('firstname','Dong')
    form.add_field('lastname','iglesias')

    # Add a fake file.
    form.add_file(
        'biography','bio.txt',
        fileHandle=io.BytesIO(b'Python developer and blogger.'))

    # Build the request,including the byte-string
    # for the data to be posted.
    data = bytes(form)
    r = request.Request('http://localhost:8080/',data=data)
    r.add_header(
        'User-agent',
        'PyMOTW(https://pymotw.com/)',
        )
    r.add_header('Content-type',form.get_content_type())
    r.add_header('Content-length',len(data))

    print()
    print('OUTGOING DATA:')
    for name,value in r.header_items():
        print('{}:{}'.format(name,value))
    print()
    print(r.data.decode('utf-8'))

    print()
    print('SERVER RESPONSE:')
    print(request.urlopen(r).read().decode('utf-8'))

MultiPartForm类可以把一个任意的表单表示为一个带附加文件的多部分MIME消息。
运行结果:
第十二章:互联网-urllib.request:网络资源访问--上传文件