Django 大文件下載實(shí)現(xiàn)過(guò)程解析
django提供文件下載時(shí),若果文件較小,解決辦法是先將要傳送的內(nèi)容全生成在內(nèi)存中,然后再一次性傳入Response對(duì)象中:
def simple_file_download(request):
# do something...
content = open("simplefile", "rb").read()
如果文件非常大時(shí),最簡(jiǎn)單的辦法就是使用靜態(tài)文件服務(wù)器,比如Apache或者Nginx服務(wù)器來(lái)處理下載。不過(guò)有時(shí)候,我們需要對(duì)用戶(hù)的權(quán)限做一下限定,或者不想向用戶(hù)暴露文件的真實(shí)地址,或者這個(gè)大內(nèi)容是臨時(shí)生成的(比如臨時(shí)將多個(gè)文件合并而成的),這時(shí)就不能使用靜態(tài)文件服務(wù)器了。
django文檔中提到,可以向HttpResponse傳遞一個(gè)迭代器,流式的向客戶(hù)端傳遞數(shù)據(jù)。
要自己寫(xiě)迭代器的話(huà),可以用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è)文件包裝器,將類(lèi)文件對(duì)象包裝成一個(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類(lèi)來(lái)代替HttpResponse對(duì)流數(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
不過(guò)不管怎么樣,使用django來(lái)處理大文件下載都不是一個(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ā)送給客戶(hù)端瀏覽器。這種方式在應(yīng)付當(dāng)今大負(fù)載網(wǎng)站會(huì)消耗更多的服務(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ù)器讀寫(xiě)文件的開(kāi)銷(xiāo),實(shí)現(xiàn)了“零拷貝”模式。 "
Apache服務(wù)器里需要mod_xsendfile模塊來(lái)實(shí)現(xiàn),而Nginx是通過(guò)稱(chēng)為X-Accel-Redirect的特性來(lái)實(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é)對(duì)用戶(hù)權(quán)限進(jìn)行判斷或者做些其它事情,然后向nginx轉(zhuǎn)發(fā)url為/protected_files/filename的請(qǐng)求,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
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python獲取局域網(wǎng)占帶寬最大3個(gè)ip的方法
這篇文章主要介紹了python獲取局域網(wǎng)占帶寬最大3個(gè)ip的方法,涉及Python解析URL參數(shù)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
利用Python如何實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)的接口自動(dòng)化測(cè)試
這篇文章主要給大家介紹了關(guān)于利用Python如何實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)的接口自動(dòng)化測(cè)試的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用python具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們來(lái)一起看看吧2018-05-05
使用python實(shí)現(xiàn)畫(huà)AR模型時(shí)序圖
今天小編就為大家分享一篇使用python實(shí)現(xiàn)畫(huà)AR模型時(shí)序圖,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
python實(shí)現(xiàn)網(wǎng)頁(yè)自動(dòng)簽到功能
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)網(wǎng)頁(yè)自動(dòng)簽到功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
python算法與數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)代碼
鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的。這篇文章主要介紹了python算法與數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)代碼,需要的朋友可以參考下2019-06-06
Python Django模板之模板過(guò)濾器與自定義模板過(guò)濾器示例
這篇文章主要介紹了Python Django模板之模板過(guò)濾器與自定義模板過(guò)濾器,結(jié)合實(shí)例形式分析了Django框架模板過(guò)濾器與自定義模板過(guò)濾器相關(guān)功能、原理、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-10-10
python二維碼操作:對(duì)QRCode和MyQR入門(mén)詳解
今天小編就為大家分享一篇python二維碼操作:對(duì)QRCode和MyQR入門(mén)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06

