将二进制数据PUT到Django时的问题

1 投票
1 回答
1056 浏览
提问于 2025-04-15 16:21

我正在尝试用Django构建一个RESTful API来分享mp3文件——先说明一下:这只是个玩具应用,不会投入生产,所以不需要考虑扩展性,也希望不需要担心版权问题。

我现在的问题是,我有一个Django视图,想把它设置为处理HTTP PUT请求的端点。PUT请求的头部会包含一些元数据,而请求的主体则只包含二进制数据。

这是我想要访问的实际视图。请注意,日志显示控制流从未进入put()方法,我认为这是正确的,虽然不算特别稳健:

class UserSong(RESTView):
    logging.debug('entering UserSong.put')
    def put(self, request, username=''):

        if request.META['Content-Type'] != 'octet/stream':
            raise Http400() 

        title = request.META['X-BD-TITLE'] if 'X-BD-TITLE' in request.META else 'title unknown'
        artist = request.META['X-BD-ARTIST'] if 'X-BD-ARTIST' in request.META else 'artist unknown' 
        album = request.META['X-BD-ALBUM'] if 'X-BD-ALBUM' in request.META else 'album unknown' 
        song_data = b6decode(request.raw_post_data)

        song = Song(title=title, artist=artist, playcount=playcount, is_sample=is_sample, song_data=song_data, album=album)
        song.save()

        return HttpResponse('OK', 'text/plain' , 201)

def __call__(self, request, *args, **kwargs):
    logging.basicConfig(filename=LOGFILE,level=logging.DEBUG)
    try:  
        if request.method == 'DELETE':
            return self.delete(request, *args, **kwargs)  
        elif request.method == 'GET':
            return self.get(request, *args, **kwargs)
        elif request.method == 'POST':
            return self.post(request, *args, **kwargs)
        elif request.method == 'PUT':
            return self.put(request, *args, **kwargs)
    except:
        raise Http404()

在测试这个的时候,我用Django的单元测试框架让测试通过了,但我不太相信这能准确模拟真实情况。所以,我打开了httplib,自己构造了一个PUT请求。这是我交互执行的代码:

>>>method = 'PUT'
>>>url = 'accounts/test/songs/'
>>>f = open('/Users/bendean/Documents/BEARBOT.mp3')
>>>data = f.read()
>>>body = data
>>>headers = {'X-BD-ARTIST' : 'BEARBOT' , 'X-BD-ALBUM':'','X-BD-TITLE':'LightningSPRKS'}
>>>headers['CONTENT-TYPE'] = 'octet/stream'
>>>import httplib
>>>c = httplib.HTTPConnection('localhost:8000')
>>>c.request(method, url, body, headers)

我得到的响应并不好看

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File  "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 880, in request
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 914, in _send_request
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 719, in send
  File "<string>", line 1, in sendall
 error: [Errno 54] Connection reset by peer

不过有时候我会得到

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 880, in request
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 914, in _send_request
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/httplib.py", line 719, in send
  File "<string>", line 1, in sendall
error: [Errno 32] Broken pipe

我对我的URL是有效的很有信心(GET请求处理得很好,谢谢)。日志显示请求实际上并没有到达处理代码。

在网上搜索后,我发现一些问题追踪器建议,问题可能出在httplib处理上传大文件时的错误(这个文件是3.7MB)。

所以,我不怕承认我在这方面有点无从下手——我该如何确定是什么导致了错误?我的请求格式正确吗(顺便说一下,我也尝试过对主体进行b64编码,结果是一样的)?从更大的角度来看,我这样做(为了测试,而不是在实际应用中)合理吗?这和开发服务器上的可配置设置有关吗?如果我把这个放到Apache上,这些问题会消失吗?非常感谢你的帮助。

1 个回答

0

看起来问题出在开发服务器处理大请求时。把程序部署到使用mod_wsgi的Apache服务器上后,这个问题就解决了。不过我对RESTful文件上传还有很多疑问...

撰写回答