cirospaciari / socketify.py

Bringing Http/Https and WebSockets High Performance servers for PyPy3 and Python3
https://www.socketify.dev
MIT License
1.44k stars 56 forks source link

multipart/form-data POST #108

Closed Wyzix33 closed 1 year ago

Wyzix33 commented 1 year ago

How can i receive POST from a multipart/form-data form having one or multiple files and some text inputs? Any example or is this even possible at this stage? Thanks

cirospaciari commented 1 year ago

Socketify don't ship a form-data parser yet but you can use on data events + https://pypi.org/project/streaming-form-data/ to get it working, I will provide an example and post here for you

Wyzix33 commented 1 year ago

It doesn't work... I've tried this:

async def upload(res, req):
    headers = {'Content-Type': 'multipart/form-data; boundary=boundary'}
    parser = StreamingFormDataParser(headers=headers)

    auth_token = ValueTarget()
    parser.register('auth_token', auth_token)
    parser.register('source',FileTarget('/tmp/file.jpg'))
    content_type = req.get_header("content-type")
    print(f"Posted to {req.get_url()} {content_type}")
    def on_data(res, chunk, is_end):
        print(f"Got chunk of data with length {len(chunk)}, is_end: {is_end}")
        parser.data_received(chunk)
        if is_end:
            res.cork_end("Thanks for the data!")

    res.on_data(on_data)

and get this:

Posted to / multipart/form-data; boundary=---------------------------34328379792375695511330156598
Got chunk of data with length 19905, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with delimiting multipart stream into parts
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 20480, is_end: False
ERROR:root:Error on data handler _parser.data_received failed with internal errors
Got chunk of data with length 6705, is_end: True
ERROR:root:Error on data handler _parser.data_received failed with internal errors

Do you have a working example for a file and a text input? Thanks

cirospaciari commented 1 year ago

Here is an working example, if you have some problems plz send me if you are using PyPy or CPython and in what OS you are, so I can check an alternative/solution for you

image

from socketify import App
from streaming_form_data import StreamingFormDataParser
from streaming_form_data.targets import ValueTarget, FileTarget
app = App()
router = app.router()

@router.post("/")
async def upload(res, req):
    print(f"Posted to {req.get_url()}")
    parser = StreamingFormDataParser(headers=req.get_headers())
    name = ValueTarget()
    parser.register('name', name)
    file = FileTarget('/tmp/file')
    file2 = FileTarget('/tmp/file2')
    parser.register('file', file)
    parser.register('file2', file2)

    def on_data(res, chunk, is_end):   
        parser.data_received(chunk)
        if is_end:
            res.cork(on_finish)

    def on_finish(res):
        print(name.value)

        print(file.multipart_filename)
        print(file.multipart_content_type)

        print(file2.multipart_filename)
        print(file2.multipart_content_type)

        res.end("Thanks for the data!")

    res.on_data(on_data)

@router.any("*")
def not_found(res, _):
    res.write_status(404).end("Not Found")

app.listen(
    3000,
    lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
)
app.run()
cirospaciari commented 1 year ago

Using some helper:

async def upload_formhelper(res, req):
    # using streaming_form_data package for parsing + helper
    from streaming_form_data import StreamingFormDataParser
    from streaming_form_data.targets import ValueTarget, FileTarget
    from helpers.form_data import get_formdata

    print(f"Posted to {req.get_url()}")
    parser = StreamingFormDataParser(headers=req.get_headers())
    name = ValueTarget()
    parser.register('name', name)
    file = FileTarget('/tmp/file')
    file2 = FileTarget('/tmp/file2')
    parser.register('file', file)
    parser.register('file2', file2)

    await get_formdata(res, parser)

    print(name.value)

    print(file.multipart_filename)
    print(file.multipart_content_type)

    print(file2.multipart_filename)
    print(file2.multipart_content_type)

    res.cork_end("Thanks for the data!")

Helper code

from streaming_form_data import StreamingFormDataParser
from socketify import Response
def get_formdata(res: Response, parser: StreamingFormDataParser):
    _dataFuture = res.app.loop.create_future()

    def is_aborted(res):
        res.aborted = True
        try:
            if not _dataFuture.done():
                _dataFuture.set_result(parser)
        except:
            pass

    def get_chunks(res, chunk, is_end):
        parser.data_received(chunk)
        if is_end:
            _dataFuture.set_result(parser)

    res.on_aborted(is_aborted)
    res.on_data(get_chunks)
    return _dataFuture
cirospaciari commented 1 year ago

Both examples working on https://github.com/cirospaciari/socketify.py/blob/main/examples/upload_or_post.py

cirospaciari commented 1 year ago

If you have some problems plz let me know and reopen this issue, I will close for now

Wyzix33 commented 1 year ago

Perfect! Thanks