# -*- 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文件服务器,修正中文目录列表,支持视频文件在线播放相关推荐

  1. 基于python的智能家居_基于Python的智能家居自动化测试脚本设计及实现

    投稿网址 http:www.VideoE.cn|<电视技术>第39 卷第5 期(总第458 期) 117 [本文献信息]沈杰,潘科,刘昕,等. 基于 Python 的智能家居自动化测试 ...

  2. 基于Python 的京东秒杀 火车票脚本

    基于Python 的京东秒杀 火车票脚本 摘要 在购买京东商品的时候,总是觉得等的太久,突然想想,要不,自己写个抢单的脚本,于是,有了这篇博文,这里来分享下自己的心得. 原理介绍 客服端不断向服务器, ...

  3. python修改ip地址_怎么更改电脑ip地址?基于 Python 爬虫的ip修改设计与实现

    怎么更改电脑ip地址?基于 Python 爬虫原理的篮球鞋选择程序的设计与实现ip修改 [摘 要]伴随着篮球鞋工艺的进步及产业升级,多类型多种类的篮球鞋出现在大众的视野当中.与此同时,消费者对篮球鞋的 ...

  4. python软件测试脚本_基于Python的实时嵌入式软件测试脚本

    摘 要:计算机技术应用的不断普及,使得实时嵌入式软件在航空.航天.工业控制.交通.医疗和军事安全等诸多领域发挥着愈来愈重要的作用.不难看出,这些领域对软件的可靠性有很严格的要求,因为该领域的软件一旦失 ...

  5. h5ai界面修改_H5ai——一个强大美观的目录列表程序

    有些时候我们要做一个个人仓库,但又不喜欢apache/nginx的默认index那种过于朴素的页面.这时候php目录列表程序的作用就发挥出来了.我个人也用过许多类似的程序,但是还是比较偏爱h5ai,因 ...

  6. python实现图像像素修改脚本

    该脚本实现了对一张图像尺寸的修改 . from torchvision import transforms from PIL import Image import matplotlib.pyplot ...

  7. 自制基于python的DoU log分析脚本

    工作中测试DoU的log需要分析,原先是使用excel,去ctrl c,ctrl v截取数据,整理格式等等.一来,这工作虽然很简单,但是非常耗时,不熟练的人(比如我)一搞搞个半天:二来,不小心还会出现 ...

  8. 基于python的简单异或脚本

    做php webshell免杀时通常会采用异或的思路,所以用个脚本辅助下,减少点手工量 编写 1.从具备ASCII码值的可打印字符选取两个字符进行异或运算,当结果在给定的字符串中时将其保存到异或列表 ...

  9. centos7配置不开启浏览器执行基于python+selenium的功能自动化脚本

    一)软件环境 python 3.7 selenium 3.11 geckodriver 0.21.0    //将其放置于/usr/bin目录下 firefox 62.0        //将其主目录 ...

最新文章

  1. 计算机软件求职信英文,计算机办公软件英文求职信
  2. 从程序员到项目经理,没有捷径可走
  3. 递归算法(二)-分治法
  4. shiro 认证和授权原理
  5. 用maven骨架新建项目以及解决速度慢的问题
  6. 【UVA10562】Undraw the Trees(括号表示法输出树+fegts读取)
  7. 课设(房屋出租系统)
  8. Python下载Wyoming怀俄明大学探空数据(数据网址更新)
  9. 锂电池充电管理芯片ic XSC01支持筋膜枪8.4V12.6V16.8充电
  10. 无处不在的微创新——验证码的故事
  11. 桥接路由器总是掉线_tp路由器桥接老掉线怎么办
  12. DDOS压力测试平台源码
  13. CTP常见问题系列之一 “CTP : 不合法的登录“
  14. 《完全用Linux工作》
  15. 视频节目 | Apache Pulsar PMC 成员讲述「开源之道」
  16. 神经网络国内外发展概况,图神经网络和神经网络
  17. Go 自定义日期时间格式解析解决方案 - 解决 `parsing time xx as xx: cannot parse xx as xx` 错误
  18. 数据库原理 西安电子科技大学(第三版) 付婷婷 第三章 课后习题答案
  19. Win Server 2022 下自动化测试,Appium连接蓝叠模拟器
  20. c语言程序设计方法及在线实践课后答案,c语言程序设计实践习题参考答案(西南师范大学出版社).doc...

热门文章

  1. 【leetcode刷题】[简单]441. 排列硬币(arranging coins)-java
  2. linux系统php安装sockets扩展
  3. php做点名系统怎么做,做了个简单点名器
  4. 使用set集合去除重复元素
  5. 【爬虫教程】吐血整理,最详细的爬虫入门教程~
  6. 让发展中国家展示清洁能源领导力
  7. jdt 解析java语句,利用JDT 回分析java 源代码
  8. ps彩色照片变黑白照片
  9. error:尝试引用已删除的函数或已显式删除函数
  10. Could not get a resource from the pool 错误解决