Django 大文件下載實(shí)現(xiàn)過程解析
django提供文件下載時(shí),若果文件較小,解決辦法是先將要傳送的內(nèi)容全生成在內(nèi)存中,然后再一次性傳入Response對象中:
def simple_file_download(request): # do something... content = open("simplefile", "rb").read()
如果文件非常大時(shí),最簡單的辦法就是使用靜態(tài)文件服務(wù)器,比如Apache或者Nginx服務(wù)器來處理下載。不過有時(shí)候,我們需要對用戶的權(quán)限做一下限定,或者不想向用戶暴露文件的真實(shí)地址,或者這個(gè)大內(nèi)容是臨時(shí)生成的(比如臨時(shí)將多個(gè)文件合并而成的),這時(shí)就不能使用靜態(tài)文件服務(wù)器了。
django文檔中提到,可以向HttpResponse傳遞一個(gè)迭代器,流式的向客戶端傳遞數(shù)據(jù)。
要自己寫迭代器的話,可以用yield:
def read_file(filename, buf_size=8192): with open(filename, "rb") as f: while True: content = f.read(buf_size) if content: yield content else: break def big_file_download(request): filename = "filename" response = HttpResponse(read_file(filename)) return response
或者使用生成器表達(dá)式,下面是django文檔中提供csv大文件下載的例子:
import csv from django.utils.six.moves import range from django.http import StreamingHttpResponse class Echo(object): """An object that implements just the write method of the file-like interface. """ def write(self, value): """Write the value by returning it, instead of storing in a buffer.""" return value def some_streaming_csv_view(request): """A view that streams a large CSV file.""" # Generate a sequence of rows. The range is based on the maximum number of # rows that can be handled by a single sheet in most spreadsheet # applications. rows = (["Row {0}".format(idx), str(idx)] for idx in range(65536)) pseudo_buffer = Echo() writer = csv.writer(pseudo_buffer) response = StreamingHttpResponse((writer.writerow(row) for row in rows), content_type="text/csv") response['Content-Disposition'] = 'attachment; filename="somefilename.csv"' return response
python也提供一個(gè)文件包裝器,將類文件對象包裝成一個(gè)迭代器:
class FileWrapper: """Wrapper to convert file-like objects to iterables""" def __init__(self, filelike, blksize=8192): self.filelike = filelike self.blksize = blksize if hasattr(filelike,'close'): self.close = filelike.close def __getitem__(self,key): data = self.filelike.read(self.blksize) if data: return data raise IndexError def __iter__(self): return self def next(self): data = self.filelike.read(self.blksize) if data: return data raise StopIteration
使用時(shí):
from django.core.servers.basehttp import FileWrapper from django.http import HttpResponse import os def file_download(request,filename): wrapper = FileWrapper(open(filename, 'rb')) response = HttpResponse(wrapper, content_type='application/octet-stream') response['Content-Length'] = os.path.getsize(path) response['Content-Disposition'] = 'attachment; filename=%s' % filename return response
django也提供了StreamingHttpResponse類來代替HttpResponse對流數(shù)據(jù)進(jìn)行處理。
壓縮為zip文件下載:
import os, tempfile, zipfile from django.http import HttpResponse from django.core.servers.basehttp import FileWrapper def send_zipfile(request): """ Create a ZIP file on disk and transmit it in chunks of 8KB, without loading the whole file into memory. A similar approach can be used for large dynamic PDF files. """ temp = tempfile.TemporaryFile() archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED) for index in range(10): filename = __file__ # Select your files here. archive.write(filename, 'file%d.txt' % index) archive.close() wrapper = FileWrapper(temp) response = HttpResponse(wrapper, content_type='application/zip') response['Content-Disposition'] = 'attachment; filename=test.zip' response['Content-Length'] = temp.tell() temp.seek(0) return response
不過不管怎么樣,使用django來處理大文件下載都不是一個(gè)很好的注意,最好的辦法是django做權(quán)限判斷,然后讓靜態(tài)服務(wù)器處理下載。
這需要使用sendfile的機(jī)制:"傳統(tǒng)的Web服務(wù)器在處理文件下載的時(shí)候,總是先讀入文件內(nèi)容到應(yīng)用程序內(nèi)存,然后再把內(nèi)存當(dāng)中的內(nèi)容發(fā)送給客戶端瀏覽器。這種方式在應(yīng)付當(dāng)今大負(fù)載網(wǎng)站會消耗更多的服務(wù)器資源。sendfile是現(xiàn)代操作系統(tǒng)支持的一種高性能網(wǎng)絡(luò)IO方式,操作系統(tǒng)內(nèi)核的sendfile調(diào)用可以將文件內(nèi)容直接推送到網(wǎng)卡的buffer當(dāng)中,從而避免了Web服務(wù)器讀寫文件的開銷,實(shí)現(xiàn)了“零拷貝”模式。 "
Apache服務(wù)器里需要mod_xsendfile模塊來實(shí)現(xiàn),而Nginx是通過稱為X-Accel-Redirect的特性來實(shí)現(xiàn)。
nginx配置文件:
# Will serve /var/www/files/myfile.tar.gz # When passed URI /protected_files/myfile.tar.gz location /protected_files { internal; alias /var/www/files; }
或者
# Will serve /var/www/protected_files/myfile.tar.gz # When passed URI /protected_files/myfile.tar.gz location /protected_files { internal; root /var/www; }
注意alias和root的區(qū)別。
django中:
response['X-Accel-Redirect']='/protected_files/%s'%filename
這樣當(dāng)向django view函數(shù)發(fā)起request時(shí),django負(fù)責(zé)對用戶權(quán)限進(jìn)行判斷或者做些其它事情,然后向nginx轉(zhuǎn)發(fā)url為/protected_files/filename的請求,nginx服務(wù)器負(fù)責(zé)文件/var/www/protected_files/filename的下載:
@login_required def document_view(request, document_id): book = Book.objects.get(id=document_id) response = HttpResponse() name=book.myBook.name.split('/')[-1] response['Content_Type']='application/octet-stream' response["Content-Disposition"] = "attachment; filename={0}".format( name.encode('utf-8')) response['Content-Length'] = os.path.getsize(book.myBook.path) response['X-Accel-Redirect'] = "/protected/{0}".format(book.myBook.name) return response
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
pygame實(shí)現(xiàn)方塊動畫實(shí)例講解
在本篇文章里小編給大家整理的是一篇關(guān)于pygame實(shí)現(xiàn)方塊動畫實(shí)例講解內(nèi)容,以后需要的朋友們可以學(xué)習(xí)參考下。2021-12-12Django1.7+python 2.78+pycharm配置mysql數(shù)據(jù)庫教程
原本感覺在Django1.7+python 2.78+pycharm環(huán)境下配置mysql數(shù)據(jù)庫是件很容易的事情,結(jié)果具體操作的時(shí)候才發(fā)現(xiàn),問題還是挺多的,這里記錄一下最終的配置結(jié)果,給需要的小伙伴參考下吧2014-11-11PyQt5入門之基于QListWidget版本實(shí)現(xiàn)圖片縮略圖列表功能
這篇文章主要介紹了PyQt5入門之基于QListWidget版本實(shí)現(xiàn)圖片縮略圖列表功能,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Python計(jì)算標(biāo)準(zhǔn)差之numpy.std和torch.std的區(qū)別
Torch自稱為神經(jīng)網(wǎng)絡(luò)中的numpy,它會將torch產(chǎn)生的tensor放在GPU中加速運(yùn)算,就像numpy會把a(bǔ)rray放在CPU中加速運(yùn)算,下面這篇文章主要給大家介紹了關(guān)于Python?Numpy計(jì)算標(biāo)準(zhǔn)差之numpy.std和torch.std區(qū)別的相關(guān)資料,需要的朋友可以參考下2022-08-08Python web開發(fā)之用Tornado框架制作簡易表白墻網(wǎng)站
這篇文章將用Python做Web開發(fā)。在Python當(dāng)中,WEB開發(fā)框架主要有三個(gè),本文將利用Tornado框架做一個(gè)簡單的表白墻網(wǎng)站,感興趣的可以了解一下2022-02-02