【问题标题】:How to serve an mp3 file with built-in python http server如何使用内置 python http 服务器提供 mp3 文件
【发布时间】:2011-08-05 16:20:53
【问题描述】:

我目前正在尝试使用 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()

【问题讨论】:

  • 你试过数据包嗅探器了吗?
  • 尝试设置 content-disposition: 附件头
  • 或者更好:使用 Python 网络框架为您完成工作 :)
  • content-disposition 不会改变任何东西。我想要一些简单的东西来分享和运行,对于 50 行的脚本来说,使用 Python 网络框架真的太过分了

标签: python http mp3 wsgi


【解决方案1】:

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

如果您愿意以 wsgi app 的形式运行它(我会推荐使用 vanilla CGI 以获得任何真正的可扩展性),您可以使用我在下面包含的脚本。

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

要运行它,您只需在 shell 中运行 python 可执行文件并浏览 8080 上机器的 IP 地址。如果您是为生产网站执行此操作,我们应该使用 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)

从 bash shell 中运行:twistd -n web --port 8080 --wsgi mp3.mp3_app 从您保存 mp3.py 的目录中(或将 mp3.py 放在 $PYTHONPATH 的某个位置)。

现在浏览到外部 ip(即http://some.ip.local:8080/),它将直接提供 mp3。

我尝试运行您发布的原始应用程序,但无法获取 mp3 的来源,它在 linux 中出现错误向我咆哮...

【讨论】:

  • 我确实在 windows 下使用 Python 2.6 和 Python 2.5 再次对其进行了测试,一切正常。这很奇怪。我故意使用 HTML5
  • 你是说 wsgi 报错了吗?如果是这样,错误是什么? wsgi 在 debian linux 下使用 lighttpd 和 python 2.5.2 为我工作
  • @Mapad,这适用于任何 html...您只需修改方法返回的内容...仅供参考,我设置了它,因此您必须浏览 http://url.local.com/mp3/,但您可以更改它目录是任何东西。顺便说一句,我以前使用过 flowplayer……如果您愿意要求客户有 Flash,这是提供音乐或视频的好方法。
  • 抱歉给您带来了困惑:我说的是我的剧本,而不是你的剧本。我已经测试过我的,它在 Windows 下工作。虽然我在 Mac OS 上的 Python 2.5 下遇到了问题。我会尽快测试您的脚本,但这需要配置服务器。我已经在 Apache 下测试了服务 MP3 并且它有效。所以我想你的脚本也能正常工作,但我希望我可以使用 Python 内置的网络服务器。该脚本旨在分发给很多人,以便让他们非常轻松地共享 MP3 文件以进行音频评估。我希望我能尽可能减少依赖
  • @Mapad...当您说共享音频文件...您的意思是直接从他们的 PC 上通过互联网为他们提供服务吗?如果是这样,这只是在一家公司内部,还是可以跨越公司边界?另请记住,许多企业 IT 部门非常严格地锁定 PC,这可能会限制您在任何随机企业笔记本电脑上运行本地网络服务器的能力
【解决方案2】:

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

例如替换行:

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

与

from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

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

【讨论】:

  • @Mike:很公平,但是 vanilla HTTPServer 的问题是它甚至不能同时处理两个连接。 IE。在关闭之前的连接之前,无法提供 MP3。
  • 谢谢...实际上这就是我在写完评论后删除评论的原因。我知道你为什么要发布这个。
  • 这仍然不是使它无法工作的原因。我已经更新了我的脚本以使用多线程服务器,但我遇到了同样的问题。我不需要多个线程来服务一个文件!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
  • 1970-01-01
  • 2017-12-20
  • 2016-11-10
  • 1970-01-01
  • 2018-05-14
相关资源
最近更新 更多