[SOLVED] Multipart files upload. File length?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
VladVons
Posts: 60
Joined: Sun Feb 12, 2017 6:49 pm
Location: Ukraine

[SOLVED] Multipart files upload. File length?

Post by VladVons » Wed Feb 03, 2021 1:51 pm

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'
Last edited by VladVons on Thu Feb 11, 2021 4:13 am, edited 2 times in total.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

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

Post by jimmo » Thu Feb 04, 2021 5:52 am

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).

VladVons
Posts: 60
Joined: Sun Feb 12, 2017 6:49 pm
Location: Ukraine

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

Post by VladVons » Thu Feb 04, 2021 8:14 pm

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

Post Reply