文档章节

300 行 python 代码的轻量级 HTTPServer 实现文件上传下载

大数据之路
 大数据之路
发布于 2012/08/08 01:51
字数 1736
阅读 3.4W
收藏 1

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

 

# !/usr/bin/env python
# coding=utf-8
# http://my.oschina.net/leejun2005/blog/71444

"""
    简介:这是一个 python 写的轻量级的文件共享服务器(基于内置的SimpleHTTPServer模块),
    支持文件上传下载,只要你安装了python(建议版本2.6~2.7,不支持3.x),
    然后去到想要共享的目录下,执行:
        python SimpleHTTPServerWithUpload.py
    或者 python SimpleHTTPServerWithUpload.py filename
"""

"""Simple HTTP Server With Upload.

This module builds on BaseHTTPServer by implementing the standard GET
and HEAD requests in a fairly straightforward manner.

"""

__version__ = "0.2"
__all__ = ["SimpleHTTPRequestHandler"]
__home_page__ = ""

import os, sys, platform, socket, struct, json
import posixpath
import BaseHTTPServer
from SocketServer import ThreadingMixIn
import threading
import urllib, urllib2
import cgi
import shutil
import mimetypes
import re
import time

reload(sys)
sys.setdefaultencoding("utf-8")

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

def echoRed(s):
    return "%s[31;1m%s%s[0m" % (chr(27), s, chr(27))

def get_ip_address(ifname=None):
    if sys.platform == 'win32':
        return socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET, socket.SOCK_DGRAM)[-1][4][0]
    else:
        import fcntl
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        return socket.inet_ntoa(fcntl.ioctl(
            s.fileno(),
            0x8915,  # SIOCGIFADDR
            struct.pack('256s', ifname[:15])
        )[20:24])

class GetWanIp:
    def getip(self):
        try:
            myip = get_ip_address(ifname="eth0")
            # myip = self.visit("http://ipinfo.io")
        except Exception, e:
            print str(e)
            myip = "127.0.0.1"
        return myip
    def visit(self ,url):
        # req = urllib2.Request(url)
        # values = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537',
        #            'Referer': 'http://ip.taobao.com/ipSearch.php',
        #            'ip': 'myip'
        #         }
        # data = urllib.urlencode(values)
        import requests
        content = requests.get('http://ipinfo.io').content
        print content
        json_content = json.loads(content)
        return json_content["ip"]

def showTips():
    print ""
    print '----------------------------------------------------------------------->> '
    try:
        port = 12345
        file_name = sys.argv[1]
        print '-------->> Please visit files or dirs use Chrome Browser:     http://' + GetWanIp().getip() + ':' + str(port)
        print "-------->> Also, You can wget download the file:    " + echoRed("wget http://" + GetWanIp().getip() + ':' + str(port) + "/" + file_name)
    except Exception, e:
        print 'You have not give a filename, plase use Chrome Browser:     http://' + GetWanIp().getip() + ':' + str(port)
        print "You can give a filename and wget download the file, Usage: " + echoRed("pywget filename")

    if not 1024 < port < 65535:  port = 8080
    print '----------------------------------------------------------------------->> '

    print ""
    # serveraddr = ('', port)
    return ('', port)


serveraddr = showTips()


def sizeof_fmt(num):
    for x in ['bytes', 'KB', 'MB', 'GB']:
        if num < 1024.0:
            return "%3.1f%s" % (num, x)
        num /= 1024.0
    return "%3.1f%s" % (num, 'TB')


def modification_date(filename):
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(os.path.getmtime(filename)))


class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    """Simple HTTP request handler with GET/HEAD/POST commands.

    This serves files from the current directory and any of its
    subdirectories.  The MIME type for files is determined by
    calling the .guess_type() method. And can reveive file uploaded
    by client.

    The GET/HEAD/POST requests are identical except that the HEAD
    request omits the actual contents of the file.

    """

    server_version = "SimpleHTTPWithUpload/" + __version__

    def do_GET(self):
        """Serve a GET request."""
        # print "....................", threading.currentThread().getName()
        f = self.send_head()
        if f:
            self.copyfile(f, self.wfile)
            f.close()

    def do_HEAD(self):
        """Serve a HEAD request."""
        f = self.send_head()
        if f:
            f.close()

    def do_POST(self):
        """Serve a POST request."""
        r, info = self.deal_post_data()
        print r, info, "by: ", self.client_address
        f = StringIO()
        f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
        f.write("<html>\n<title>Upload Result Page</title>\n")
        f.write("<body>\n<h2>Upload Result Page</h2>\n")
        f.write("<hr>\n")
        if r:
            f.write("<strong>Success:</strong>")
        else:
            f.write("<strong>Failed:</strong>")
        f.write(info)
        f.write("<br><a href=\"%s\">back</a>" % self.headers['referer'])
        f.write("<hr><small>Powered By: bones7456, check new version at ")
        f.write("<a href=\"http://li2z.cn/?s=SimpleHTTPServerWithUpload\">")
        f.write("here</a>.</small></body>\n</html>\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.send_header("Content-Length", str(length))
        self.end_headers()
        if f:
            self.copyfile(f, self.wfile)
            f.close()

    def deal_post_data(self):
        boundary = self.headers.plisttext.split("=")[1]
        remainbytes = int(self.headers['content-length'])
        line = self.rfile.readline()
        remainbytes -= len(line)
        if not boundary in line:
            return (False, "Content NOT begin with boundary")
        line = self.rfile.readline()
        remainbytes -= len(line)
        fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line)
        if not fn:
            return (False, "Can't find out file name...")
        path = self.translate_path(self.path)
        osType = platform.system()
        try:
            if osType == "Linux":
                fn = os.path.join(path, fn[0].decode('gbk').encode('utf-8'))
            else:
                fn = os.path.join(path, fn[0])
        except Exception, e:
            return (False, "文件名请不要用中文,或者使用IE上传中文名的文件。")
        while os.path.exists(fn):
            fn += "_"
        line = self.rfile.readline()
        remainbytes -= len(line)
        line = self.rfile.readline()
        remainbytes -= len(line)
        try:
            out = open(fn, 'wb')
        except IOError:
            return (False, "Can't create file to write, do you have permission to write?")

        preline = self.rfile.readline()
        remainbytes -= len(preline)
        while remainbytes > 0:
            line = self.rfile.readline()
            remainbytes -= len(line)
            if boundary in line:
                preline = preline[0:-1]
                if preline.endswith('\r'):
                    preline = preline[0:-1]
                out.write(preline)
                out.close()
                return (True, "File '%s' upload success!" % fn)
            else:
                out.write(preline)
                preline = line
        return (False, "Unexpect Ends of data.")

    def send_head(self):
        """Common code for GET and HEAD commands.

        This sends the response code and MIME headers.

        Return value is either a file object (which has to be copied
        to the outputfile by the caller unless the command was HEAD,
        and must be closed by the caller under all circumstances), or
        None, in which case the caller has nothing further to do.

        """
        path = self.translate_path(self.path)
        f = None
        if os.path.isdir(path):
            if not self.path.endswith('/'):
                # redirect browser - doing basically what apache does
                self.send_response(301)
                self.send_header("Location", self.path + "/")
                self.end_headers()
                return None
            for index in "index.html", "index.htm":
                index = os.path.join(path, index)
                if os.path.exists(index):
                    path = index
                    break
            else:
                return self.list_directory(path)
        ctype = self.guess_type(path)
        try:
            # Always read in binary mode. Opening files in text mode may cause
            # newline translations, making the actual size of the content
            # transmitted *less* than the content-length!
            f = open(path, 'rb')
        except IOError:
            self.send_error(404, "File not found")
            return None
        self.send_response(200)
        self.send_header("Content-type", ctype)
        fs = os.fstat(f.fileno())
        self.send_header("Content-Length", str(fs[6]))
        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
        self.end_headers()
        return f

    def list_directory(self, path):
        """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().

        """
        try:
            list = os.listdir(path)
        except os.error:
            self.send_error(404, "No permission to list directory")
            return None
        list.sort(key=lambda a: a.lower())
        f = StringIO()
        displaypath = cgi.escape(urllib.unquote(self.path))
        f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
        f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
        f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
        f.write("<hr>\n")
        f.write("<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
        f.write("<input name=\"file\" type=\"file\"/>")
        f.write("<input type=\"submit\" value=\"upload\"/>")
        f.write("&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp")
        f.write("<input type=\"button\" value=\"HomePage\" onClick=\"location='/'\">")
        f.write("</form>\n")
        f.write("<hr>\n<ul>\n")
        for name in list:
            fullname = os.path.join(path, name)
            colorName = displayname = linkname = name
            # Append / for directories or @ for symbolic links
            if os.path.isdir(fullname):
                colorName = '<span style="background-color: #CEFFCE;">' + name + '/</span>'
                displayname = name
                linkname = name + "/"
            if os.path.islink(fullname):
                colorName = '<span style="background-color: #FFBFFF;">' + name + '@</span>'
                displayname = name
                # Note: a link to a directory displays with @ and links with /
            filename = os.getcwd() + '/' + displaypath + displayname
            f.write(
                '<table><tr><td width="60%%"><a href="%s">%s</a></td><td width="20%%">%s</td><td width="20%%">%s</td></tr>\n'
                % (urllib.quote(linkname), colorName,
                   sizeof_fmt(os.path.getsize(filename)), modification_date(filename)))
        f.write("</table>\n<hr>\n</body>\n</html>\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.send_header("Content-Length", str(length))
        self.end_headers()
        return f

    def translate_path(self, path):
        """Translate a /-separated PATH to the local filename syntax.

        Components that mean special things to the local file system
        (e.g. drive or directory names) are ignored.  (XXX They should
        probably be diagnosed.)

        """
        # abandon query parameters
        path = path.split('?', 1)[0]
        path = path.split('#', 1)[0]
        path = posixpath.normpath(urllib.unquote(path))
        words = path.split('/')
        words = filter(None, words)
        path = os.getcwd()
        for word in words:
            drive, word = os.path.splitdrive(word)
            head, word = os.path.split(word)
            if word in (os.curdir, os.pardir): continue
            path = os.path.join(path, word)
        return path

    def copyfile(self, source, outputfile):
        """Copy all data between two file objects.

        The SOURCE argument is a file object open for reading
        (or anything with a read() method) and the DESTINATION
        argument is a file object open for writing (or
        anything with a write() method).

        The only reason for overriding this would be to change
        the block size or perhaps to replace newlines by CRLF
        -- note however that this the default server uses this
        to copy binary data as well.

        """
        shutil.copyfileobj(source, outputfile)

    def guess_type(self, path):
        """Guess the type of a file.

        Argument is a PATH (a filename).

        Return value is a string of the form type/subtype,
        usable for a MIME Content-type header.

        The default implementation looks the file's extension
        up in the table self.extensions_map, using application/octet-stream
        as a default; however it would be permissible (if
        slow) to look inside the data to make a better guess.

        """

        base, ext = posixpath.splitext(path)
        if ext in self.extensions_map:
            return self.extensions_map[ext]
        ext = ext.lower()
        if ext in self.extensions_map:
            return self.extensions_map[ext]
        else:
            return self.extensions_map['']

    if not mimetypes.inited:
        mimetypes.init()  # try to read system mime.types
    extensions_map = mimetypes.types_map.copy()
    extensions_map.update({
        '': 'application/octet-stream',  # Default
        '.py': 'text/plain',
        '.c': 'text/plain',
        '.h': 'text/plain',
    })


class ThreadingServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
    pass


def test(HandlerClass=SimpleHTTPRequestHandler,
         ServerClass=BaseHTTPServer.HTTPServer):
    BaseHTTPServer.test(HandlerClass, ServerClass)


if __name__ == '__main__':
    # test()

    # 单线程
    # srvr = BaseHTTPServer.HTTPServer(serveraddr, SimpleHTTPRequestHandler)

    # 多线程
    srvr = ThreadingServer(serveraddr, SimpleHTTPRequestHandler)

    srvr.serve_forever()

REF: 

[1] httpserver

httpserver
=======================================
This httpserver is a enhanced version of SimpleHTTPServer. 
It was write in python, I use some code from bottle[https://github.com/defnull/bottle] 
It support resuming download, you can set the document root, it has more 
friendly error hit, and it can handle mimetype gracefully

https://github.com/lerry/httpserver/blob/master/httpserver.py

[2] py2_SimpleHTTPServerWithUpload

https://github.com/jJayyyyyyy/cs/blob/master/just%20for%20fun/file_transfer/http/py2_SimpleHTTPServerWithUpload/SimpleHTTPServerWithUpload.py

[3] py3_SimpleHTTPServerWithUpload.py

https://github.com/jJayyyyyyy/cs/blob/master/just%20for%20fun/file_transfer/http/py3_SimpleHTTPServerWithUpload.py

[4] python3重写SimpleHTTPServerWithUpload

https://jjayyyyyyy.github.io/2016/10/07/reWrite_SimpleHTTPServerWithUpload_with_python3.html

[5] 支持upload文件的SimpleHTTPServer

http://buptguo.com/2015/11/07/simplehttpserver-with-upload-file/

本文转载自:https://github.com/tualatrix/tools/blob/master/SimpleHTTPServerWithUpload.py

大数据之路
粉丝 1636
博文 514
码字总数 328539
作品 0
武汉
架构师
私信 提问
加载中

评论(10)

辛文飞
你这程序老牛逼了,完美解决我的需求
不得了啦
不得了啦
赞赞赞!!!
开源中国首席打酱油啊哎滴
开源中国首席打酱油啊哎滴
可以了 ,python3版本13
https://gist.github.com/0312birdzhang/a56566e6395d14f10c96
开源中国首席打酱油啊哎滴
开源中国首席打酱油啊哎滴
楼主,我改了一下 python3版本的https://gist.github.com/7dc7b8fde43b64ecd004.git
现在还不能上传文件,查看下载是可以的
_帅的被人砍
_帅的被人砍
找不到 module fcntl
luxf
luxf
用了 3Q
大数据之路
大数据之路 博主

引用来自“久欠幸福”的评论

引用来自“xrzs”的评论

引用来自“久欠幸福”的评论

找不到 module fcntl

请检查版本:2.7+ 是没问题的。。。

我的是2.7.3,我比对了一下文章里面的代码与 github 中的代码,发现代码不一样,github里面的代码是没有问题的,本文中的代码貌似不行,其中 53行 import fcntl 在源码中是没有的

该脚本我增加了一些功能,如不需要,请将相应内容删除即可。
久欠幸福
久欠幸福

引用来自“xrzs”的评论

引用来自“久欠幸福”的评论

找不到 module fcntl

请检查版本:2.7+ 是没问题的。。。

我的是2.7.3,我比对了一下文章里面的代码与 github 中的代码,发现代码不一样,github里面的代码是没有问题的,本文中的代码貌似不行,其中 53行 import fcntl 在源码中是没有的
大数据之路
大数据之路 博主

引用来自“久欠幸福”的评论

找不到 module fcntl

请检查版本:2.7+ 是没问题的。。。
久欠幸福
久欠幸福
找不到 module fcntl
[svc]nginx限制客户端上传附件的大小

300 行 python 代码的轻量级 HTTPServer 实现文件上传下载 系统环境 直接运行访问即可 http://192.168.2.11:12345 准备nginx反代环境 后启动,在主机上加hosts. 访问 upload.maotai.com 传输一...

osc_t4d5tw3o
2018/03/11
3
0
一个轻量级的框架

试了一个HTTPServer,RequestHandle的数据响应服务,响应速度比较慢(只是还可以接受而已);又试了一个SocketServer - Client 响应非常快可以作为一组分布式应用的数据服务内核;Web-Client的...

Tim Spac
2010/04/25
251
0
15 个简单、有趣而实用的 单行 HTTP Server

不少语言或服务开发框架都内置了简单的 Web Server 供我们方便的调试使用。比如有时候我们需要调试单个 PHP 页面而不想搭建一套完整的 PHP 环境,亦或者我们只是临时的共享一下文件而不想搭建...

xrzs
2016/05/15
4K
0
python实例 ---- 简单的HttpServer

通常地我们要在不同平台间共享文件,samba,ftp,cifs,ntfs的设置都是有点复杂的, 我们可以使用python提供的httpserver来提供基于http方式跨平台的文件共享。 一 命令行启动简单的httpser...

jack_Meng
04/26
0
0
初探Python CGI:搭建CGI编程调试环境

“CGI(Common Gateway Interface,通用网关接口) 是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。”(百度百科)Python语言自带CGI处理模块,可以通过编写python ...

SamYjy
2018/12/02
802
0

没有更多内容

加载失败,请刷新页面

加载更多

PDF如何添加下划线?迅捷PDF编辑器一键添加

“在PDF文件中如何添加下划线?”最近,很多办公室小伙伴都向小编咨询这样一个问题。我们常常需要接触、使用到PDF文件,通过查看、阅读、编辑PDF文件以处理各种各样的学习、工作任务。当我们...

dawda
1分钟前
0
0
go中gin框架+realize实现边写代码边编译,热更新

最近看到了热加载,相关的,就搜索了goland实现热加载 发现了一个插件realize https://github.com/oxequa/realize 然后,为了自己撸代码更方便,配合gin写个教程 1.准备 go get github.com/...

osc_ho8dcqsx
1分钟前
0
0
CAP理论的理解

转自:https://www.cnblogs.com/mingorun/p/11025538.html CAP理论的理解 CAP理论作为分布式系统的基础理论,它描述的是一个分布式系统在以下三个特性中: 一致性(Consistency) 可用性(Ava...

osc_5rgbamh9
2分钟前
0
0
求所有科目都大于80分的学生姓名

   蠢蠢的我=》 select t1.name from ( select name,count(*) as num from table t where fenshu>80 group by name) t1join( select name,count(kecheng) as num from table group ......

osc_gk4myeyk
3分钟前
0
0
Memcache(1.1)Memcache 基本概述与架构概述

【1】基本概念介绍 官网:https://memcached.org/ 【1.1】memcache与memcached memcache:是早期使用的,与php结合的,是Php中常用的一个原生插件,完全在php框架内开发的 memcached:是建立...

osc_7ie26pzn
5分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部