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

发布于 2024-11-01 13:29:34 字数 2566 浏览 4 评论 0原文

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

I am currently trying to serve MP3 Files using Python. The problem is that I can only play the MP3 once. Afterwards media controls stop responding and I need to reload entirely the page to be able to listen again to the MP3. (tested in Chrome)

Problem: running the script below, and entering http://127.0.0.1/test.mp3 on my browser will return an MP3 files which can be replayed only if I refresh the page

Notes:

  • Saving the page as HTML and loading it directly with Chrome (without Python server) would make the problem disappear.

  • Serving the file with Apache would solve the problem, but this is overkilled: I want to make the script very easy to use and not require installing Apache.

Here is the code I use:

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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冬天的雪花 2024-11-08 13:29:34

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

例如,将行:替换

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

from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

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

BaseServer is single-threaded, you should use either ForkingMixIn or ThreadingMixIn to support multiple connections.

For example replace line:

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

with

from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

server = ThreadedHTTPServer(('', 80), MyHandler)
错々过的事 2024-11-08 13:29:34

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

如果您愿意将其作为 wsgi 应用程序 运行(我会推荐使用普通 CGI 来实现真正的可扩展性) ),您可以使用我在下面包含的脚本。

我冒昧修改了你的源代码...这符合上面的假设..顺便说一句,你应该花一些时间检查你的 html 是否合理合规...这将有助于确保你获得更好的跨浏览器兼容性..原始版本没有 标签...我的(下面)是严格的原型 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'^

从 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 中向我发出错误......

, mp3_html), (r'mp3/?

从 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 中向我发出错误......

, 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 中向我发出错误......

EDIT: I wrote much of this before I realized Mapadd only planned to use this in a lab. WSGI probably is not required for his use case.

If you are willing to run this as a wsgi app (which I would recommend over vanilla CGI for any real scalability), you can use the script I have included below.

I took the liberty of modifying your source... this works with the assumptions above.. btw, you should spend some time checking that your html is reasonably compliant... this will help ensure that you get better cross-browser compatibility... the original didn't have <head> or <body> tags... mine (below) is strictly prototype html, and could be improved.

To run this, you just run the python executable in your shell and surf to the ipaddress of the machine on 8080. If you were doing this for a production website, we should be using lighttpd or apache for serving files, but since this is simply for lab use, the embedded wsgi reference server should be fine. Substitute the WSGIServer line at the bottom of the file if you want to run in apache or lighttpd.

Save as 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'^

Run from the bash shell with: twistd -n web --port 8080 --wsgi mp3.mp3_app from the directory where you saved mp3.py (or just put mp3.py somewhere in $PYTHONPATH).

Now surf to the external ip (i.e. http://some.ip.local:8080/) and it will serve the mp3 directly.

I tried running your original app as it was posted, and could not get it to source the mp3, it barked at me with an error in linux...

, mp3_html), (r'mp3/?

Run from the bash shell with: twistd -n web --port 8080 --wsgi mp3.mp3_app from the directory where you saved mp3.py (or just put mp3.py somewhere in $PYTHONPATH).

Now surf to the external ip (i.e. http://some.ip.local:8080/) and it will serve the mp3 directly.

I tried running your original app as it was posted, and could not get it to source the mp3, it barked at me with an error in linux...

, 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)

Run from the bash shell with: twistd -n web --port 8080 --wsgi mp3.mp3_app from the directory where you saved mp3.py (or just put mp3.py somewhere in $PYTHONPATH).

Now surf to the external ip (i.e. http://some.ip.local:8080/) and it will serve the mp3 directly.

I tried running your original app as it was posted, and could not get it to source the mp3, it barked at me with an error in linux...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文