zoukankan      html  css  js  c++  java
  • 启动一个支持文件上传的HTTP-Server

    Python实现,源码来自网络,代码内部有作者信息。

    HTTP方式共享文件,对于不需要用户名和密码验证的系统非常方便。通过浏览器就可以实现文件上传和下载。非常适合用作测试系统的脚手架。

    对于系统使用curl命令行也可以轻松实现文件上传。wget实现文件下载。

    nohup python SimpleHTTPServerWithUpload.py 8088 &  # 在8088端口启动这个http-server服务
      1 #!/usr/bin/env python
      2  
      3 """Simple HTTP Server With Upload.
      4  
      5 This module builds on BaseHTTPServer by implementing the standard GET
      6 and HEAD requests in a fairly straightforward manner.
      7  
      8 """
      9  
     10  
     11 __version__ = "0.1"
     12 __all__ = ["SimpleHTTPRequestHandler"]
     13 __author__ = "bones7456"
     14 __home_page__ = "http://li2z.cn/"
     15  
     16 import os
     17 import posixpath
     18 import BaseHTTPServer
     19 import urllib
     20 import cgi
     21 import shutil
     22 import mimetypes
     23 import re
     24 try:
     25     from cStringIO import StringIO
     26 except ImportError:
     27     from StringIO import StringIO
     28  
     29  
     30 class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
     31  
     32     """Simple HTTP request handler with GET/HEAD/POST commands.
     33  
     34     This serves files from the current directory and any of its
     35     subdirectories.  The MIME type for files is determined by
     36     calling the .guess_type() method. And can reveive file uploaded
     37     by client.
     38  
     39     The GET/HEAD/POST requests are identical except that the HEAD
     40     request omits the actual contents of the file.
     41  
     42     """
     43  
     44     server_version = "SimpleHTTPWithUpload/" + __version__
     45  
     46     def do_GET(self):
     47         """Serve a GET request."""
     48         f = self.send_head()
     49         if f:
     50             self.copyfile(f, self.wfile)
     51             f.close()
     52  
     53     def do_HEAD(self):
     54         """Serve a HEAD request."""
     55         f = self.send_head()
     56         if f:
     57             f.close()
     58  
     59     def do_POST(self):
     60         """Serve a POST request."""
     61         r, info = self.deal_post_data()
     62         print r, info, "by: ", self.client_address
     63         f = StringIO()
     64         f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
     65         f.write("<html>
    <title>Upload Result Page</title>
    ")
     66         f.write("<body>
    <h2>Upload Result Page</h2>
    ")
     67         f.write("<hr>
    ")
     68         if r:
     69             f.write("<strong>Success:</strong>")
     70         else:
     71             f.write("<strong>Failed:</strong>")
     72         f.write(info)
     73         f.write("<br><a href="%s">back</a>" % self.headers['referer'])
     74         f.write("<hr><small>Powered By: bones7456, check new version at ")
     75         f.write("<a href="http://li2z.cn/?s=SimpleHTTPServerWithUpload">")
     76         f.write("here</a>.</small></body>
    </html>
    ")
     77         length = f.tell()
     78         f.seek(0)
     79         self.send_response(200)
     80         self.send_header("Content-type", "text/html")
     81         self.send_header("Content-Length", str(length))
     82         self.end_headers()
     83         if f:
     84             self.copyfile(f, self.wfile)
     85             f.close()
     86          
     87     def deal_post_data(self):
     88         boundary = self.headers.plisttext.split("=")[1]
     89         remainbytes = int(self.headers['content-length'])
     90         line = self.rfile.readline()
     91         remainbytes -= len(line)
     92         if not boundary in line:
     93             return (False, "Content NOT begin with boundary")
     94         line = self.rfile.readline()
     95         remainbytes -= len(line)
     96         fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line)
     97         if not fn:
     98             return (False, "Can't find out file name...")
     99         path = self.translate_path(self.path)
    100         fn = os.path.join(path, fn[0])
    101         while os.path.exists(fn):
    102             fn += "_"
    103         line = self.rfile.readline()
    104         remainbytes -= len(line)
    105         line = self.rfile.readline()
    106         remainbytes -= len(line)
    107         try:
    108             out = open(fn, 'wb')
    109         except IOError:
    110             return (False, "Can't create file to write, do you have permission to write?")
    111                  
    112         preline = self.rfile.readline()
    113         remainbytes -= len(preline)
    114         while remainbytes > 0:
    115             line = self.rfile.readline()
    116             remainbytes -= len(line)
    117             if boundary in line:
    118                 preline = preline[0:-1]
    119                 if preline.endswith('
    '):
    120                     preline = preline[0:-1]
    121                 out.write(preline)
    122                 out.close()
    123                 return (True, "File '%s' upload success!" % fn)
    124             else:
    125                 out.write(preline)
    126                 preline = line
    127         return (False, "Unexpect Ends of data.")
    128  
    129     def send_head(self):
    130         """Common code for GET and HEAD commands.
    131  
    132         This sends the response code and MIME headers.
    133  
    134         Return value is either a file object (which has to be copied
    135         to the outputfile by the caller unless the command was HEAD,
    136         and must be closed by the caller under all circumstances), or
    137         None, in which case the caller has nothing further to do.
    138  
    139         """
    140         path = self.translate_path(self.path)
    141         f = None
    142         if os.path.isdir(path):
    143             if not self.path.endswith('/'):
    144                 # redirect browser - doing basically what apache does
    145                 self.send_response(301)
    146                 self.send_header("Location", self.path + "/")
    147                 self.end_headers()
    148                 return None
    149             for index in "index.html", "index.htm":
    150                 index = os.path.join(path, index)
    151                 if os.path.exists(index):
    152                     path = index
    153                     break
    154             else:
    155                 return self.list_directory(path)
    156         ctype = self.guess_type(path)
    157         try:
    158             # Always read in binary mode. Opening files in text mode may cause
    159             # newline translations, making the actual size of the content
    160             # transmitted *less* than the content-length!
    161             f = open(path, 'rb')
    162         except IOError:
    163             self.send_error(404, "File not found")
    164             return None
    165         self.send_response(200)
    166         self.send_header("Content-type", ctype)
    167         fs = os.fstat(f.fileno())
    168         self.send_header("Content-Length", str(fs[6]))
    169         self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
    170         self.end_headers()
    171         return f
    172  
    173     def list_directory(self, path):
    174         """Helper to produce a directory listing (absent index.html).
    175  
    176         Return value is either a file object, or None (indicating an
    177         error).  In either case, the headers are sent, making the
    178         interface the same as for send_head().
    179  
    180         """
    181         try:
    182             list = os.listdir(path)
    183         except os.error:
    184             self.send_error(404, "No permission to list directory")
    185             return None
    186         list.sort(key=lambda a: a.lower())
    187         f = StringIO()
    188         displaypath = cgi.escape(urllib.unquote(self.path))
    189         f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
    190         f.write("<html>
    <title>Directory listing for %s</title>
    " % displaypath)
    191         f.write("<body>
    <h2>Directory listing for %s</h2>
    " % displaypath)
    192         f.write("<hr>
    ")
    193         f.write("<form ENCTYPE="multipart/form-data" method="post">")
    194         f.write("<input name="file" type="file"/>")
    195         f.write("<input type="submit" value="upload"/></form>
    ")
    196         f.write("<hr>
    <ul>
    ")
    197         for name in list:
    198             fullname = os.path.join(path, name)
    199             displayname = linkname = name
    200             # Append / for directories or @ for symbolic links
    201             if os.path.isdir(fullname):
    202                 displayname = name + "/"
    203                 linkname = name + "/"
    204             if os.path.islink(fullname):
    205                 displayname = name + "@"
    206                 # Note: a link to a directory displays with @ and links with /
    207             f.write('<li><a href="%s">%s</a>
    '
    208                     % (urllib.quote(linkname), cgi.escape(displayname)))
    209         f.write("</ul>
    <hr>
    </body>
    </html>
    ")
    210         length = f.tell()
    211         f.seek(0)
    212         self.send_response(200)
    213         self.send_header("Content-type", "text/html")
    214         self.send_header("Content-Length", str(length))
    215         self.end_headers()
    216         return f
    217  
    218     def translate_path(self, path):
    219         """Translate a /-separated PATH to the local filename syntax.
    220  
    221         Components that mean special things to the local file system
    222         (e.g. drive or directory names) are ignored.  (XXX They should
    223         probably be diagnosed.)
    224  
    225         """
    226         # abandon query parameters
    227         path = path.split('?',1)[0]
    228         path = path.split('#',1)[0]
    229         path = posixpath.normpath(urllib.unquote(path))
    230         words = path.split('/')
    231         words = filter(None, words)
    232         path = os.getcwd()
    233         for word in words:
    234             drive, word = os.path.splitdrive(word)
    235             head, word = os.path.split(word)
    236             if word in (os.curdir, os.pardir): continue
    237             path = os.path.join(path, word)
    238         return path
    239  
    240     def copyfile(self, source, outputfile):
    241         """Copy all data between two file objects.
    242  
    243         The SOURCE argument is a file object open for reading
    244         (or anything with a read() method) and the DESTINATION
    245         argument is a file object open for writing (or
    246         anything with a write() method).
    247  
    248         The only reason for overriding this would be to change
    249         the block size or perhaps to replace newlines by CRLF
    250         -- note however that this the default server uses this
    251         to copy binary data as well.
    252  
    253         """
    254         shutil.copyfileobj(source, outputfile)
    255  
    256     def guess_type(self, path):
    257         """Guess the type of a file.
    258  
    259         Argument is a PATH (a filename).
    260  
    261         Return value is a string of the form type/subtype,
    262         usable for a MIME Content-type header.
    263  
    264         The default implementation looks the file's extension
    265         up in the table self.extensions_map, using application/octet-stream
    266         as a default; however it would be permissible (if
    267         slow) to look inside the data to make a better guess.
    268  
    269         """
    270  
    271         base, ext = posixpath.splitext(path)
    272         if ext in self.extensions_map:
    273             return self.extensions_map[ext]
    274         ext = ext.lower()
    275         if ext in self.extensions_map:
    276             return self.extensions_map[ext]
    277         else:
    278             return self.extensions_map['']
    279  
    280     if not mimetypes.inited:
    281         mimetypes.init() # try to read system mime.types
    282     extensions_map = mimetypes.types_map.copy()
    283     extensions_map.update({
    284         '': 'application/octet-stream', # Default
    285         '.py': 'text/plain',
    286         '.c': 'text/plain',
    287         '.h': 'text/plain',
    288         })
    289  
    290  
    291 def test(HandlerClass = SimpleHTTPRequestHandler,
    292          ServerClass = BaseHTTPServer.HTTPServer):
    293     BaseHTTPServer.test(HandlerClass, ServerClass)
    294  
    295 if __name__ == '__main__':
    296     test()
  • 相关阅读:
    【160406 24:00】四则运算 4(结对开发 2)
    【160319 24:00】四则运算 3(结对开发 1)
    【160313 18:00】四则运算 2 的单元测试
    【160312 18:00】四则运算 2
    【Web前端Talk】React-loadable 进行代码分割的基本使用
    【Web前端Talk】无聊吗?写个【飞机大战】来玩吧(下篇)
    【Web前端Talk】“用数据说话,从埋点开始”-带你理解前端的三种埋点
    【Web前端Talk】无聊吗?写个【飞机大战】来玩吧(上篇)
    windows环境下搭建vue开发环境
    Sublime Text3添加css兼容前缀插件
  • 原文地址:https://www.cnblogs.com/geektown/p/5122414.html
Copyright © 2011-2022 走看看