如何使用内置的python http服务器提供mp3文件

时间:2011-04-14 15:58:08

标签: python http mp3 wsgi

我目前正在尝试使用Python提供MP3文件。问题是我只能播放一次MP3。之后媒体控制停止响应,我需要完全重新加载页面才能再次收听MP3。 (在Chrome中测试)

问题:运行下面的脚本,在我的浏览器上输入http://127.0.0.1/test.mp3将返回一个MP3文件,只有刷新页面才可以重播

注意:

  • 将页面保存为HTML并直接使用Chrome加载(不使用Python服务器)会使问题消失。

  • 使用Apache提供文件可以解决问题,但这是过度的:我想使脚本非常易于使用而不需要安装Apache

以下是我使用的代码:

import string
import os
import urllib
import socket

# Setup web server import string,cgi,time
import string,cgi,time
from os import curdir, sep
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import hashlib

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        try:
            # serve mp3 files
            if self.path.endswith(".mp3"):
                print curdir + sep + self.path
                f = open(curdir + sep + self.path, 'rb')
                st = os.fstat( f.fileno() )
                length = st.st_size
                data = f.read()
                md5 = hashlib.md5()
                md5.update(data)
                md5_key = self.headers.getheader('If-None-Match')
                if md5_key:
                  if md5_key[1:-1] == md5.hexdigest():
                    self.send_response(304)
                    self.send_header('ETag', '"{0}"'.format(md5.hexdigest()))
                    self.send_header('Keep-Alive', 'timeout=5, max=100')
                    self.end_headers()
                    return

                self.send_response(200)
                self.send_header('Content-type',    'audio/mpeg')
                self.send_header('Content-Length', length )
                self.send_header('ETag', '"{0}"'.format(md5.hexdigest()))
                self.send_header('Accept-Ranges', 'bytes')
                self.send_header('Last-Modified', time.strftime("%a %d %b %Y %H:%M:%S GMT",time.localtime(os.path.getmtime('test.mp3'))))
                self.end_headers()
                self.wfile.write(data)
                f.close()
            return
        except IOError:
           self.send_error(404,'File Not Found: %s' % self.path)

from SocketServer import ThreadingMixIn
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

if __name__ == "__main__":
    try:
       server = ThreadedHTTPServer(('', 80), MyHandler)
       print 'started httpserver...'
       server.serve_forever()
    except KeyboardInterrupt:
       print '^C received, shutting down server'
       server.socket.close()

2 个答案:

答案 0 :(得分:2)

编辑:在我意识到Mapadd计划在实验室中使用它之前,我写了很多。他的用例可能不需要WSGI。

如果您愿意将其作为wsgi app运行(我建议使用vanilla CGI获得任何真正的可扩展性),您可以使用我在下面提供的脚本。

我冒昧地修改你的来源...这适用于上面的假设..顺便说一句,你应该花一些时间检查你的html是否合理合规......这将有助于确保你获得更好的跨浏览器兼容性...原始版本没有<head><body>标签...我的(下方)是严格的原型html,可以改进。

要运行它,你只需在你的shell中运行python可执行文件并在8080上冲到机器的ipaddress。如果你是为生产网站做这个,我们应该使用lighttpd或apache来提供文件,但是这只是供实验室使用,嵌入式wsgi参考服务器应该没问题。如果要在apache或lighttpd中运行,请替换文件底部的WSGIServer行。

另存为mp3.py

from webob import Request
import re
import os
import sys

####
#### Run with:
#### twistd -n web --port 8080 --wsgi mp3.mp3_app

_MP3DIV = """<div id="musicHere"></div>"""

_MP3EMBED = """<embed src="mp3/" loop="true" autoplay="false" width="145" height="60"></embed>"""

_HTML = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head></head><body> Hello %s %s</body></html> ''' % (_MP3DIV, _MP3EMBED)

def mp3_html(environ, start_response):
    """This function will be mounted on "/" and refer the browser to the mp3 serving URL."""

    start_response('200 OK', [('Content-Type', 'text/html')])
    return [_HTML]

def mp3_serve(environ, start_response):
    """Serve the MP3, one chunk at a time with a generator"""
    file_path = "/file/path/to/test.mp3"
    mimetype = "application/x-mplayer2"
    size = os.path.getsize(file_path)
    headers = [
        ("Content-type", mimetype),
        ("Content-length", str(size)),
    ]
    start_response("200 OK", headers)
    return send_file(file_path, size)

def send_file(file_path, size):
    BLOCK_SIZE = 4096
    fh = open(file_path, 'r')
    while True:
        block = fh.read(BLOCK_SIZE)
        if not block:
            fh.close()
            break
        yield block

def _not_found(environ,start_response):
    """Called if no URL matches."""
    start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
    return ['Not Found']

def mp3_app(environ,start_response):
    """
    The main WSGI application. Dispatch the current request to
    the functions andd store the regular expression
    captures in the WSGI environment as  `mp3app.url_args` so that
    the functions from above can access the url placeholders.

    If nothing matches call the `not_found` function.
    """
    # map urls to functions
    urls = [
        (r'^$', mp3_html),
        (r'mp3/?$', mp3_serve),
    ]
    path = environ.get('PATH_INFO', '').lstrip('/')
    for regex, callback in urls:
        match = re.search(regex, path)
        if match is not None:
            # assign http environment variables...
            environ['mp3app.url_args'] = match.groups()
            return callback(environ, start_response)
    return _not_found(environ, start_response)

从保存mp3.py的目录中使用:twistd -n web --port 8080 --wsgi mp3.mp3_app从bash shell运行(或者只将mp3.py放在$PYTHONPATH中的某处)。

现在冲到外部ip(即http://some.ip.local:8080/),它将直接为mp3服务。

我尝试运行原来的应用程序,因为它已经发布了,并且无法获取它来源mp3,它在我的Linux中出错了...

答案 1 :(得分:2)

BaseServer是单线程的,您应该使用ForkingMixInThreadingMixIn来支持多个连接。

例如替换行:

server = HTTPServer(('', 80), MyHandler)

from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

server = ThreadedHTTPServer(('', 80), MyHandler)