2013-10-09 76 views
1

我繼承下面的Django視圖代碼,使用由另一個web服務來服務的輸出數據的下載版本:「上關閉的文件I/O操作」

def index(request): 
    # ... (snip) ... 
    data = base64.decodestring(request.POST['data']) 
    filename = request.POST['filename'] 

    wrapper = FileWrapper(StringIO(data)) 

    response = HttpResponse(wrapper, content_type=guess_type(str(filename))[0]) 

    response['Content-Length'] = len(data) 
    response['Content-Disposition'] = "attachment; filename=" + filename 

    return response 

函數本身 - 寫入反對Django 1.0 - 升級到1.5後仍然正常工作。不幸的是,覆蓋這一觀點現在測試失敗:

def testDownload(self): 
     self.client.login(username='test', password='test') 

     real = 'abc' * 100 
     data = base64.encodestring(real) 
     response = self.client.post("/api/v1/download/", {'data': data, 'filename': 'out.jpg'}) 

     self.assertEqual(real, response.content) 
     self.assertEqual(response['Content-Disposition'], 'attachment; filename=out.jpg') 

和錯誤:

Traceback (most recent call last): 
    File "/home/fred/.secret_projects/final/gerbils/tests/amf.py", line 548, in testDownload 
    self.assertEqual(real, response.content) 
    File "/home/fred/.virtualenvs/cunning_plot/lib/python2.7/site-packages/django/http/response.py", line 282, in content 
    self._consume_content() 
    File "/home/carl/.virtualenvs/cunning_plot/lib/python2.7/site-packages/django/http/response.py", line 278, in _consume_content 
    self.content = b''.join(self.make_bytes(e) for e in self._container) 
    File "/home/carl/.virtualenvs/cunning_plot/lib/python2.7/site-packages/django/http/response.py", line 278, in <genexpr> 
    self.content = b''.join(self.make_bytes(e) for e in self._container) 
    File "/usr/lib64/python2.7/wsgiref/util.py", line 30, in next 
    data = self.filelike.read(self.blksize) 
    File "/usr/lib64/python2.7/StringIO.py", line 127, in read 
    _complain_ifclosed(self.closed) 
    File "/usr/lib64/python2.7/StringIO.py", line 40, in _complain_ifclosed 
    raise ValueError, "I/O operation on closed file" 
ValueError: I/O operation on closed file 

所以..任何想法?我無法看到testDownload()index()中的任何內容,在需要閱讀之前必須「關閉」StringIO。如果有什麼事情,它不會影響非測試情況嗎?

非常困惑。幫助讚賞。

+0

我不確定什麼'FileWrapper'是,但你應該能夠做到你想要的沒有它。嘗試'file_content = StringIO(data)'並將'file_content'傳遞給'HttpResponse'然後重新運行你的測試。 – abstractpaper

+0

感謝您的評論!它是'django.core.servers.basehttp.FileWrapper',僅供參考。例如參考[這個片段](http://djangosnippets.org/snippets/365/),我認爲這就是我的前任使用它的原因。 – supervacuo

回答

4

查看close被調用的一個簡單方法就是將子類StringIO並在關閉函數中放置一個斷點。

class CustomStringIO(StringIO): 
    def close(self): 
     import pdb; pdb.set_trace() 
     super(CustomStringIO, self).close() 

堆棧這是

-> response = self.client.post("/test/", {'data': data, 'filename': 'out.jpg'}) 
    ...\venv\lib\site-packages\django\test\client.py(463)post() 
-> response = super(Client, self).post(path, data=data, content_type=content_type, **extra) 
    ...\venv\lib\site-packages\django\test\client.py(297)post() 
-> return self.request(**r) 
    ...\venv\lib\site-packages\django\test\client.py(406)request() 
-> response = self.handler(environ) 
    ...\venv\lib\site-packages\django\test\client.py(119)__call__() 
-> response.close()     # will fire request_finished 
    ...\venv\lib\site-packages\django\http\response.py(233)close() 
-> closable.close() 
> \testapp\views.py(11)close() 
-> super(CustomStringIO, self).close() 

它看起來像測試客戶端是封閉的反應,這反過來又調用close上FileWrapper然後調用收盤StringIO。這是在你真正到達response.content之前。

你有什麼需要的原因FileWrapper?由於HttpResponse需要字符串內容,並且base64.decodestring返回二進制字符串,因此您似乎可以直接將data直接傳遞給HttpResponse,而不必創建StringIOFileWrapper

+0

非常好的想法子類'StringIO',謝謝(併發布堆棧跟蹤以及錦上添花的蛋糕)。我以爲我已經嘗試過切割'StringIO'和'FileWrapper',但我剛剛這樣做,它確實修復了它。非凡:謝謝! – supervacuo

+0

不能獎賞你的賞金4個小時,但會盡快這樣做。 – supervacuo