1

I'm attempting to write a Twisted Webserver in Python 3.6 that can upload multiple files but being fairly new to both Python and things Web I have ran into an issue I don't understand and I also do not find any good examples relating to multiple file upload.

With the following code I get

from twisted.web import server, resource
from twisted.internet import reactor, endpoints
import cgi


class Counter(resource.Resource):
    isLeaf = True
    numberRequests = 0

    def render_GET(self, request):
        print("GET " + str(self.numberRequests))
        self.numberRequests += 1
        # request.setHeader(b"content-type", b"text/plain")
        # content = u"I am request #{}\n".format(self.numberRequests)
        content = """<html>
        <body>
        <form enctype="multipart/form-data" method="POST">
            Text: <input name="text1" type="text" /><br />
            File: <input name="file1" type="file" multiple /><br />
            <input type="submit" />
        </form>
        </body>
        </html>"""
        print(request.uri)


        return content.encode("ascii")

    def render_POST(selfself, request):
        postheaders = request.getAllHeaders()
        try:
            postfile = cgi.FieldStorage(
                fp=request.content,
                headers=postheaders,
                environ={'REQUEST_METHOD': 'POST',
                        # 'CONTENT_TYPE': postheaders['content-type'], Gives builtins.KeyError: 'content-type'
                         }
            )
        except Exception as e:
            print('something went wrong: ' + str(e))

        filename = postfile["file"].filename #file1 tag also does not work
        print(filename)

        file = request.args["file"][0] #file1 tag also does not work



endpoints.serverFromString(reactor, "tcp:1234").listen(server.Site(Counter()))

reactor.run()

Error log

C:\Users\theuser\AppData\Local\conda\conda\envs\py36\python.exe C:/Users/theuser/PycharmProjects/myproject/twweb.py
GET 0
b'/'
# My comment POST starts here
Unhandled Error
Traceback (most recent call last):
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\site-packages\twisted\web\http.py", line 1614, in dataReceived
    finishCallback(data[contentLength:])
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\site-packages\twisted\web\http.py", line 2029, in _finishRequestBody
    self.allContentReceived()
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\site-packages\twisted\web\http.py", line 2104, in allContentReceived
    req.requestReceived(command, path, version)
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\site-packages\twisted\web\http.py", line 866, in requestReceived
    self.process()
--- <exception caught here> ---
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\site-packages\twisted\web\server.py", line 195, in process
    self.render(resrc)
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\site-packages\twisted\web\server.py", line 255, in render
    body = resrc.render(self)
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\site-packages\twisted\web\resource.py", line 250, in render
    return m(request)
  File "C:/Users/theuser/PycharmProjects/myproject/twweb.py", line 42, in render_POST
    filename = postfile["file"].filename #file1 tag also does not work
  File "C:\Users\theuser\AppData\Local\conda\conda\envs\py36\lib\cgi.py", line 604, in __getitem__
    raise KeyError(key)
builtins.KeyError: 'file'

I don't understand how to get hold of the file or files so I can save them uploaded after the form Submit in render_POST. This post SO appears to not have this problem. The final app should be able to do this asynchronous for multiple users but before that I would be happy to get just a simple app working.

Using conda on Windows 10 with Python 3.6

bits
  • 309
  • 2
  • 11

1 Answers1

1

FieldStorage in Python 3+ returns a dict with keys as bytes, not strings. So you must access keys like this:

postfile[ b"file" ]

Notice that the key is appended with b"". It's a tad bit confusing if you're new to Python and are unaware of the changes between Python 3 and 2 strings.

Also I answered a similar question a while back but was unable to get it working properly on Python 3.4 but I can't remember what exactly didn't work. Hopefully you don't run into any issues using 3.6.

notorious.no
  • 4,919
  • 3
  • 20
  • 34
  • With the b'content-type' I get passed the problem in cgi.FieldStorage. But I can't manage to get the fields for file later on, it parses out 2 keys name and filename that has the fields inside them but I can't manage to get them out of that. I switched to Python 2.7 and then I can get this to work so I still think the problem from 3.4 exist in 3.6 – bits Nov 23 '17 at 22:13