title: Save file received from POST request in Python
body: <p>I'm trying to implement an upload feature to the basic <a href="https://github.com/python/cpython/blob/af441df3ff0eab7bce782011cf702a8a9005da1f/Lib/http/server.py" rel="nofollow noreferrer">http.server</a> Python module.</p>
  <p>So far, I've created a new class named <code>SimpleHTTPRequestHandlerWithUpload</code> which inherits from <code>SimpleHTTPRequestHandler</code> and added an upload section to <code>list_directory()</code>. The next step would be creating a <code>do_POST()</code> method, which handles the request and saves the file inside the current working directory. However, I have no idea how to do this. I looked at <a href="https://gist.github.com/UniIsland/3346170" rel="nofollow noreferrer">UniIsland's code</a> on GitHub but I can't understand what he did and the code is very old. I also read <a href="https://stackoverflow.com/questions/36622564/how-to-save-a-text-file-received-in-a-post-request-using-python">this question</a> and tried to implement it in my code.</p>
  <p>It kind of works, but the file is &quot;littered&quot; with headers. This does not pose a big problem on txt files, but it corrupts all of the other file extensions.</p>
  <p><strong>I'd like to know how to remove the headers, save the uploaded file inside the current working directory with its original name and check if the upload was successful or not.</strong></p>
  <p>This is my code:</p>
  <pre><code>__version__ = '0.1'
  
  import http.server
  import html
  import io
  import os
  import socket  # For gethostbyaddr()
  import sys
  import urllib.parse
  import contextlib
  
  from http import HTTPStatus
  
  
  class SimpleHTTPRequestHandlerWithUpload(http.server.SimpleHTTPRequestHandler):
  
      server_version = 'SimpleHTTPWithUpload/' + __version__
  
      def do_POST(self):
          &quot;&quot;&quot;Serve a POST request.&quot;&quot;&quot;
          data = self.rfile.read(int(self.headers['content-length']))
          with open('file.txt', 'wb') as file:
              file.write(data)
          r = []
          enc = sys.getfilesystemencoding()
          r.append('&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD HTML 3.2 Final//EN&quot;&gt;')
          r.append(&quot;&lt;html&gt;\n&lt;title&gt;Upload Result Page&lt;/title&gt;\n&quot;)
          r.append(&quot;&lt;body&gt;\n&lt;h2&gt;Upload Result Page&lt;/h2&gt;\n&quot;)
          r.append(&quot;&lt;/body&gt;\n&lt;/html&gt;&quot;)
          encoded = '\n'.join(r).encode(enc, 'surrogateescape')
          f = io.BytesIO()
          f.write(encoded)
          f.seek(0)
          self.send_response(HTTPStatus.OK)
          self.send_header(&quot;Content-type&quot;, &quot;text/html&quot;)
          self.send_header(&quot;Content-Length&quot;, str(len(encoded)))
          self.end_headers()
          if f:
              self.copyfile(f, self.wfile)
              f.close()
  
      def list_directory(self, path):
          &quot;&quot;&quot;Helper to produce a directory listing (absent index.html).
          Return value is either a file object, or None (indicating an
          error).  In either case, the headers are sent, making the
          interface the same as for send_head().
          &quot;&quot;&quot;
          try:
              list = os.listdir(path)
          except OSError:
              self.send_error(
                  HTTPStatus.NOT_FOUND,
                  'No permission to list directory')
              return None
          list.sort(key=lambda a: a.lower())
          r = []
          try:
              displaypath = urllib.parse.unquote(self.path,
                                                 errors='surrogatepass')
          except UnicodeDecodeError:
              displaypath = urllib.parse.unquote(path)
          displaypath = html.escape(displaypath, quote=False)
          enc = sys.getfilesystemencoding()
          title = 'Directory listing for %s' % displaypath
          r.append('&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; '
                   '&quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;')
          r.append('&lt;html&gt;\n&lt;head&gt;')
          r.append('&lt;meta http-equiv=&quot;Content-Type&quot; '
                   'content=&quot;text/html; charset=%s&quot;&gt;' % enc)
          r.append('&lt;title&gt;%s&lt;/title&gt;\n&lt;/head&gt;' % title)
          r.append('&lt;body&gt;\n&lt;h1&gt;%s&lt;/h1&gt;' % title)
          r.append('&lt;hr&gt;\n&lt;ul&gt;')
          for name in list:
              fullname = os.path.join(path, name)
              displayname = linkname = name
              # Append / for directories or @ for symbolic links
              if os.path.isdir(fullname):
                  displayname = name + '/'
                  linkname = name + '/'
              if os.path.islink(fullname):
                  displayname = name + '@'
                  # Note: a link to a directory displays with @ and links with /
              r.append('&lt;li&gt;&lt;a href=&quot;%s&quot;&gt;%s&lt;/a&gt;&lt;/li&gt;' % (urllib.parse.quote(linkname, errors='surrogatepass'),
                                                         html.escape(displayname, quote=False)))
          r.append('&lt;/ul&gt;\n&lt;hr&gt;\n')
          r.append('&lt;form id=&quot;upload&quot; enctype=&quot;multipart/form-data&quot; method=&quot;post&quot; action=&quot;#&quot;&gt;\n'
                   '&lt;input id=&quot;fileupload&quot; name=&quot;file&quot; type=&quot;file&quot; /&gt;\n'
                   '&lt;input type=&quot;submit&quot; value=&quot;Submit&quot; id=&quot;submit&quot; /&gt;\n'
                   '&lt;/form&gt;')
          r.append('\n&lt;hr&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n')
          encoded = '\n'.join(r).encode(enc, 'surrogateescape')
          f = io.BytesIO()
          f.write(encoded)
          f.seek(0)
          self.send_response(HTTPStatus.OK)
          self.send_header('Content-type', 'text/html; charset=%s' % enc)
          self.send_header('Content-Length', str(len(encoded)))
          self.end_headers()
          return f
  
  
  if __name__ == '__main__':
      class DualStackServer(http.server.ThreadingHTTPServer):
          def server_bind(self):
              # suppress exception when protocol is IPv4
              with contextlib.suppress(Exception):
                  self.socket.setsockopt(
                      socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
              return super().server_bind()
  
      http.server.test(
          HandlerClass=SimpleHTTPRequestHandlerWithUpload,
          ServerClass=DualStackServer
      )
  </code></pre>
  <p>If you want to test it, just run the script on your machine, open a web browser on a different machine and type in the address bar <code>&lt;IP_ADDRESS_1&gt;:8000</code> where <code>IP_ADDRESS_1</code> is the IP of the machine you're running the code on.</p>
  <p>Please, tell me if there's something wrong with it other than the <code>do_POST()</code> method. I'm a new Python programmer and I'm trying to improve my software design skills in general. Thank you!</p>
  <hr />
  <p>EDIT: I figured out how to remove the headers and save the file with its original name. However, the script hangs on <code>data = self.rfile.readlines()</code> until I close the browser tab and then works well. I don't know what to do. It seems I have to send some sort of EOF to notify <code>readlines()</code> that I'm finished sending the file but I have no clue how to do it. I also can't figure out how to check if the file has been uploaded successfully or not. Any help is appreciated!</p>
  <p>Updated <code>do_POST()</code> method:</p>
  <pre><code>def do_POST(self):
      &quot;&quot;&quot;Serve a POST request.&quot;&quot;&quot;
      data = self.rfile.readlines()
      filename = re.findall(r'Content-Disposition.*name=&quot;file&quot;; filename=&quot;(.*)&quot;', str(data[1]))
      if len(filename) == 1:
          filename = ''.join(filename)
      else:
          return
      data = data[4:-2]
      data = b''.join(data)
      with open(filename, 'wb') as file:
          file.write(data)
      r = []
      enc = sys.getfilesystemencoding()
      r.append('&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; '
               '&quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;')
      r.append('&lt;html&gt;\n&lt;title&gt;Upload result page&lt;/title&gt;\n')
      r.append('&lt;body&gt;\n&lt;h2&gt;Upload result page&lt;/h2&gt;\n')
      r.append('&lt;/body&gt;\n&lt;/html&gt;')
      encoded = '\n'.join(r).encode(enc, 'surrogateescape')
      f = io.BytesIO()
      f.write(encoded)
      f.seek(0)
      self.send_response(HTTPStatus.OK)
      self.send_header('Content-type', 'text/html')
      self.send_header('Content-Length', str(len(encoded)))
      self.end_headers()
      if f:
          self.copyfile(f, self.wfile)
          f.close()
  </code></pre>