基于Python SimpleHTTPServer.py的修改脚本:HTTP文件服务器,修正中文目录列表,支持视频文件在线播放
生活随笔
收集整理的這篇文章主要介紹了
基于Python SimpleHTTPServer.py的修改脚本:HTTP文件服务器,修正中文目录列表,支持视频文件在线播放
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
# -*- coding: gbkimport SimpleHTTPServer
import BaseHTTPServer
import time
import SocketServer
import os
import threading
import socket
import re#下面的導入從SimpleHTTPServer.py復制:
import posixpath
import urllib
import urlparse
import cgi
import sys
import shutil
import mimetypes
try:from cStringIO import StringIO
except ImportError:from StringIO import StringIOPORT = 8888class MyThreadingHTTPServer(SocketServer.ThreadingTCPServer):allow_reuse_address = 1def server_bind(self):"""Override server_bind to store the server name."""SocketServer.TCPServer.server_bind(self)host, port = self.socket.getsockname()[:2]self.server_name = socket.getfqdn(host)self.server_port = port#Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
class MyHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):def dumpRequestHeaders(self):print 'dumpRequestHeaders: raw_requestline=%s \nheaders=\n%s' % (self.raw_requestline,self.headers)def copyfile_by_range(self, fin, fout, start, end):print "copyfile_by_range: start=%d end=%d" % (start, end)READ_BUFFER_SIZE = 4*1024;fin.seek(start, os.SEEK_SET)if end<0: #代表原始Range請求未指定完整范圍,只指定了開始位置buf = fin.read(READ_BUFFER_SIZE) #FIXME:健壯性fix,如果讀到內容小于size參數?需要判斷len(buf)if len(buf)!=READ_BUFFER_SIZE:print "copyfile_by_range: len(buf)!=READ_BUFFER_SIZE 1 len(buf)=%d" % (len(buf))while buf:fout.write(buf)fout.flush()buf = fin.read(READ_BUFFER_SIZE)if len(buf)==0:break #??if len(buf)!=READ_BUFFER_SIZE:print "copyfile_by_range: len(buf)!=READ_BUFFER_SIZE 2 len(buf)=%d" % (len(buf))fout.write(buf)breakelse:bytes_left = end-start+1while bytes_left >= READ_BUFFER_SIZE:buf = fin.read(READ_BUFFER_SIZE)if len(buf)!=READ_BUFFER_SIZE:print "copyfile_by_range: len(buf)!=READ_BUFFER_SIZE 3 len(buf)=%d" % (len(buf))fout.write(buf)bytes_left = bytes_left - READ_BUFFER_SIZEif bytes_left>0:buf = fin.read(bytes_left)if len(buf)!=bytes_left:print "copyfile_by_range: len(buf)!=bytes_left len(buf)=%d bytes_left=" % (len(buf), bytes_left)fout.write(buf) def do_GET(self):self.dumpRequestHeaders() #用于查看客戶端瀏覽器的User-Agent設置;##SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)f, range = self.send_head() #原來的send_head這個函數實現有點莫名其妙?if f:if range:#注意,響應頭部已經在send_head()里設置完成了,這里只需要調整io讀寫指針self.copyfile_by_range(f, self.wfile, range[0], range[1])else:self.copyfile(f, self.wfile)f.close()#重載SimpleHTTPServer.py里的實現,以實現:(1)按修改日期排序(2)正確顯示中文#TODO:支持更多查詢參數?html輸出代碼美化?def list_directory(self, path):"""Helper to produce a directory listing (absent index.html).Return value is either a file object, or None (indicating anerror). In either case, the headers are sent, making theinterface 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())def compare_by_modtime(x, y):stat_x = os.stat(path + "/" + x)stat_y = os.stat(path + "/" + y)if stat_x.st_mtime < stat_y.st_mtime:return -1elif stat_x.st_mtime > stat_y.st_mtime:return 1else:return 0list.sort(lambda x,y: compare_by_modtime(y,x)) #最近修改的排在前面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<ul>\n")for name in list:fullname = os.path.join(path, name)displayname = linkname = name# Append / for directories or @ for symbolic linksif 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 /f.write('<li><a href="%s">%s</a>|<a href="/playvideo?path=%s">播放</a>\n'% (urllib.quote(linkname), cgi.escape(displayname), urllib.quote( os.path.join(self.path, name))))#self.path是瀏覽器請求路徑,而path是本地文件系統路徑f.write("</ul>\n<hr>\n</body>\n</html>\n")length = f.tell()f.seek(0)self.send_response(200)encoding = "gbk" #sys.getfilesystemencoding()self.send_header("Content-Type", "text/html; charset=%s" % encoding)self.send_header("Content-Length", str(length))self.end_headers()return f#TODO:支持Range請求,這樣可以提供基于HTTP的視頻流媒體服務def send_head(self):"""overwrite send_head to set Last-Modified & Expires to disable browser cache;"""unquoted_path = urllib.unquote(self.path)print "send_head: self.path=%s unquoted_path=%s" % (self.path, unquoted_path)PLAYVIDEO_REQUEST = re.compile(r'/playvideo\?path=(.+)$')m = PLAYVIDEO_REQUEST.match(unquoted_path)if m: #TODO: 重構這里的代碼 video_path = m.group(1)print "send_head: video_path=%s" % video_pathself.send_response(200)self.send_header("Content-Type", "text/html")self.end_headers()self.wfile.write('<video src="%s" controls></video>' % video_path) #注意,這個地方不需要urllib.quotereturn (None,None)path = self.translate_path(self.path)f = Noneif os.path.isdir(path):if not self.path.endswith('/'):# redirect browser - doing basically what apache doesself.send_response(301)self.send_header("Location", self.path + "/")self.wfile.flush()time.sleep(1)self.end_headers()return (None,None)for index in "index.html", "index.htm":index = os.path.join(path, index)if os.path.exists(index):path = indexbreakelse:return (self.list_directory(path), None)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')#Get file size:f.seek(0, os.SEEK_END)filesize = f.tell()f.seek(0, os.SEEK_SET)#TODO: 檢查原始請求是否指定了Range頭部if self.headers.has_key("Range"): range_value = self.headers["Range"]print "send_head: range_value=[%s]" % range_value#直接使用正則表達式匹配: Range: bytes=100-HTTP_RANGE_HEADER = re.compile(r'bytes=([0-9]+)\-(([0-9]+)?)')m = re.match(HTTP_RANGE_HEADER, range_value)if m:start_str = m.group(1)start = int(start_str)end_str = m.group(2)end = -1if len(end_str)>0:end = int(end_str)#現在可以寫Range響應頭部了:self.send_response(206, "Partial Content")self.send_header("Content-Type", ctype)self.send_header("Content-Length", str(filesize)) #or str(file_stat[6])self.send_header("Accept-Ranges", "bytes")if end<0:content_range_header_value = "bytes %d-%d/%d" % (start, filesize-1, filesize)else:content_range_header_value = "bytes %d-%d/%d" % (start, end, filesize)self.send_header("Content-Range", content_range_header_value)print "send_head: ok, serve 206 for Range request %s-%s,Content-Range=%s" % (start_str, end_str, content_range_header_value)self.send_header("Connection", "close")self.end_headers()return (f, [start, end])else:print "send_head: error! INVALID Range request header!!"self.send_error(400, "Bad Request")self.wfile.flush()self.end_headers()return (None,None) except IOError:self.send_error(404, "File not found")return (None,None)self.send_response(200)self.send_header("Content-Type", ctype)file_stat = os.fstat(f.fileno())self.send_header("Content-Length", str(file_stat[6]))#self.send_header("Last-Modified", self.date_time_string(file_stat.st_mtime))self.send_header("Last-Modified", self.date_time_string(time.time()))self.send_header("Expires", self.date_time_string(time.time()+5))self.send_header("Cache-control", "no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, no-transform")self.send_header("Pragma", "no-cache")self.end_headers()return (f, None)s = MyThreadingHTTPServer(("", PORT), MyHTTPRequestHandler)
s.serve_forever()
#已知問題:當Chrome以仿真模擬移動設備播放視頻時,會出現奇怪的net::ERR_CONTENT_LENGTH_MISMATCH錯誤,桌面情況下Chrome和Firefox都可以正常播放視頻,但是Edge不可以。
總結
以上是生活随笔為你收集整理的基于Python SimpleHTTPServer.py的修改脚本:HTTP文件服务器,修正中文目录列表,支持视频文件在线播放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在电脑上安装Linux系统步骤
- 下一篇: 如何优雅地管理微信数据库?