Page 1 of 1

[SOLVED] Multipart files upload. File length?

Posted: Wed Feb 03, 2021 1:51 pm
by VladVons
i upload two files and dont know how to get thier context length and data
should i search in POST every time data boundary ? ----WebKitFormBoundarywjRojsBoPY7GpAWu
is there some 'BOUNDARY-content-length' like a HTTP head content-length ?

there are many heavy weight python libraries that handles file uploading, but they are too big to fit in memory.

q5.txt (data QQQQQ)
w5.txt (data WWWW)
(or some jpeg binary file)

HTML code for loading multiple files

Code: Select all

<html>
  <body>
     <form enctype = "multipart/form-data" action = "/upload" method = "post">
         <p>File: <input type = "file" name = "filename" multipart /></p>
         <p><input type = "submit" value = "Upload" /></p>
    </form>
  </body>
</html>
After pressing upload button i read socket and get data:

HTTP head
'content-length': 431
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundarywjRojsBoPY7GpAWu'

POST data

Code: Select all

b'------WebKitFormBoundarywjRojsBoPY7GpAWu\r\n
Content-Disposition: form-data; name="filename"; filename="w5.txt"\r\n
Content-Type: text/plain\r\n
\r\n
WWWWW\r\n
------WebKitFormBoundarywjRojsBoPY7GpAWu\r\n
Content-Disposition: form-data; name="filename"; filename="q5.txt"\r\n
Content-Type: text/plain\r\n
\r\n
QQQQQ\r\n
------WebKitFormBoundarywjRojsBoPY7GpAWu\r\n
Content-Disposition: form-data; name="_Path"\r\n
\r\n
Test\r\n
------WebKitFormBoundarywjRojsBoPY7GpAWu--\r\n'

Re: Multipart files upload. How to get the file length or its data?

Posted: Thu Feb 04, 2021 5:52 am
by jimmo
VladVons wrote:
Wed Feb 03, 2021 1:51 pm
should i search in POST every time data boundary ? ----WebKitFormBoundarywjRojsBoPY7GpAWu
Yes. You need to find out what the boundary token is from the headers, and then read the input line by line until you get the boundary on a line by its own. See https://tools.ietf.org/html/rfc2046#section-5.1.1 for the details.

If you're uploading binary files then you need something a bit cleverer (i.e. read byte by byte and detect newlines manually).

Re: Multipart files upload. How to get the file length or its data?

Posted: Thu Feb 04, 2021 8:14 pm
by VladVons
thanks jimmo
my async multiple files upload implementation

Code: Select all

import uasyncio as asyncio

class TMulUpload():
    @staticmethod
    def ParseRec(aStr: str) -> dict:
        Res = {}
        for Item in aStr.split(';'):
            Arr = Item.split('=')
            if (len(Arr) == 2):
                Res[Arr[0].strip().lower()] = Arr[1].strip()
            else:
                Res[Item.strip()] = ''
        return Res

     '''
     In: 
     aHead - HTTP head dict
     aPath - directory to save file
     Out:
     return uploaded file path and size as dict
     '''
    async def Upload(self, aReader: asyncio.StreamReader, aHead: dict, aPath: str) -> dict:
        Res = {}

        Rec = self.ParseRec(aHead.get('content-type', ''))
        if (not Rec):
            return Res

        Boundary = bytes('--' + Rec.get('boundary'), 'utf-8')
        BoundLen = len(Boundary)

        ContLen = int(aHead.get('content-length', '0'))
        FileN = ''; FileH = None
        InHead = True
        Len = 0
        while (Len < ContLen):
            Line = await aReader.readline()
            Len += len(Line)

            if (Line[:BoundLen] == Boundary):
                InHead = True

                if (FileH):
                    FileH.seek(0, 2)
                    Res[FileN] = FileH.tell()

                    FileH.close()
                    FileH = None
            else:
                if (InHead):
                    if (Line == b'\r\n'):
                        InHead = False
                    else:
                        Arr = Line.decode("utf-8").split(':')
                        if (Arr[0].lower() == 'content-disposition'):
                            Rec = self.ParseRec(Arr[1])
                            FileN = Rec.get('filename')
                            if (FileN):
                                FileN = aPath + '/' + FileN.replace('"', '')
                                FileH = open(FileN, 'w')
                else:
                    if (FileH):
                        FileH.write(Line)
                        await asyncio.sleep_ms(10)
        return Res