Django 实现下载文件功能的示例
基于Django建立的网站,如果提供文件下载功能,最简单的方式莫过于将静态文件交给Nginx等处理,但有些时候,由于网站本身逻辑,需要通过Django提供下载功能,如页面数据导出功能(下载动态生成的文件)、先检查用户权限再下载文件等。因此,有必要研究一下文件下载功能在Django中的实现。
最简单的文件下载功能的实现
将文件流放入HttpResponse对象即可,如:
deffile_download(request): #dosomething... withopen('file_name.txt')asf: c=f.read() returnHttpResponse(c)
这种方式简单粗暴,适合小文件的下载,但如果这个文件非常大,这种方式会占用大量的内存,甚至导致服务器崩溃
更合理的文件下载功能
Django的HttpResponse对象允许将迭代器作为传入参数,将上面代码中的传入参数c换成一个迭代器,便可以将上述下载功能优化为对大小文件均适合;而Django更进一步,推荐使用StreamingHttpResponse对象取代HttpResponse对象,StreamingHttpResponse对象用于将文件流发送给浏览器,与HttpResponse对象非常相似,对于文件下载功能,使用StreamingHttpResponse对象更合理。
因此,更加合理的文件下载功能,应该先写一个迭代器,用于处理文件,然后将这个迭代器作为参数传递给StreaminghttpResponse对象,如:
fromdjango.httpimportStreamingHttpResponse defbig_file_download(request): #dosomething... deffile_iterator(file_name,chunk_size=512): withopen(file_name)asf: whileTrue: c=f.read(chunk_size) ifc: yieldc else: break the_file_name="file_name.txt" response=StreamingHttpResponse(file_iterator(the_file_name)) returnresponse
文件下载功能再次优化
上述的代码,已经完成了将服务器上的文件,通过文件流传输到浏览器,但文件流通常会以乱码形式显示到浏览器中,而非下载到硬盘上,因此,还要在做点优化,让文件流写入硬盘。优化很简单,给StreamingHttpResponse对象的Content-Type和Content-Disposition字段赋下面的值即可,如:
response['Content-Type']='application/octet-stream' response['Content-Disposition']='attachment;filename="test.pdf"'
完整代码如下:
fromdjango.httpimportStreamingHttpResponse defbig_file_download(request): #dosomething... deffile_iterator(file_name,chunk_size=512): withopen(file_name)asf: whileTrue: c=f.read(chunk_size) ifc: yieldc else: break the_file_name="big_file.pdf" response=StreamingHttpResponse(file_iterator(the_file_name)) response['Content-Type']='application/octet-stream' response['Content-Disposition']='attachment;filename="{0}"'.format(the_file_name) returnresponse
具体导出文件格式
导出Excel表格
1.首先是直接导出Excel表格
首先获取要导出的数据、以列表方式保存。
然后将数据写入到Excel,以流的方式返回到页面下载。
importxlwt importio importjson fromdjango.httpimportHttpResponse defset_style(name,height,bold=False): style=xlwt.XFStyle()#初始化样式 font=xlwt.Font()#为样式创建字体 font.name=name#'TimesNewRoman' font.bold=bold font.color_index=000 font.height=height style.font=font #设置单元格边框 #borders=xlwt.Borders() #borders.left=6 #borders.right=6 #borders.top=6 #borders.bottom=6 #style.borders=borders #设置单元格背景颜色 #pattern=xlwt.Pattern() #设置其模式为实型 #pattern.pattern=pattern.SOLID_PATTERN #设置单元格背景颜色 #pattern.pattern_fore_colour=0x00 #style.pattern=pattern returnstyle defwrite_excel(data,name,header): #打开一个Excel工作簿 file=xlwt.Workbook() #新建一个sheet,如果对一个单元格重复操作,会引发异常,所以加上参数cell_overwrite_ok=True table=file.add_sheet(name,cell_overwrite_ok=True) ifdataisNone: returnfile #写标题栏 row0=[u'业务',u'状态',u'北京',u'上海',u'广州',u'深圳',u'状态小计'] foriinrange(0,len(row0)): table.write_merge(0,0,i,i,row0[i],set_style('TimesNewRoman',220,True)) table.write_merge(0,2,7,9,"单元格合并",set_style('TimesNewRoman',220,True)) """ table.write_merge(x,x+m,y,w+n,string,sytle) x表示行,y表示列,m表示跨行个数,n表示跨列个数,string表示要写入的单元格内容,style表示单元格样式。其中,x,y,w,h,都是以0开始计算的。 """ l=0 n=len(header) #写入数据 forlineindata: foriinrange(n): table.write(l,i,line[header[i]]) l+=1 #直接保存文件 #file.save("D:/excel_name.xls") #写入IO res=get_excel_stream(file) #设置HttpResponse的类型 response=HttpResponse(content_type='application/vnd.ms-excel') fromurllibimportparse response['Content-Disposition']='attachment;filename='+parse.quote("excel_name")+'.xls' #将文件流写入到response返回 response.write(res) returnresponse defget_excel_stream(file): #StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。 excel_stream=io.BytesIO() #这点很重要,传给save函数的不是保存文件名,而是一个BytesIO流(在内存中读写) file.save(excel_stream) #getvalue方法用于获得写入后的byte将结果返回给re res=excel_stream.getvalue() excel_stream.close() returnres
2.导出json文件
导出json文件不像Excel那么麻烦,只需要拼接json格式数据即可,直接导出到本地还是很简单,但是导出到网页,怎么像导出excel一样不保存到本地,直接将流返回?
defwrite_json(data): try: json_stream=get_json_stream(data) response=HttpResponse(content_type='application/json') fromurllibimportparse response['Content-Disposition']='attachment;filename='+parse.quote("test")+'.json' response.write(json_stream) returnresponse exceptExceptionase: print(e) defget_json_stream(data): #开始这里我用ByteIO流总是出错,但是后来参考廖雪峰网站用StringIO就没问题 file=io.StringIO() data=json.dumps(data) file.write(data) res=file.getvalue() file.close() returnres
3.导出压缩包
由于导出两个文件无法同时都返回,所以考虑将这两个文件放入包中,然后将包以流的方式返回。
思考?此时导出的是zip包中,我怎么将这两个文件流写入zip中,好像有点不太合理。后来在老大指导下先将要打包的文件保存到本地,打包到zip后,将本地的文件删除,随后将该zip文件流读取,写入到response,返回zip文件流。
defwrite_zip(e_data,j_data,export_name): try: #保存到本地文件 #返回文件名,注意此时保存的方法和前面导出保存的json、excel文件区别 j_name=write_json(j_data,export_name[1]) e_name=write_excel(e_data,export_name[1]) #本地文件写入zip,重命名,然后删除本地临时文件 z_name='export.zip' z_file=zipfile.ZipFile(z_name,'w') z_file.write(j_name) z_file.write(e_name) os.remove(j_name) os.remove(e_name) z_file.close() #再次读取zip文件,将文件流返回,但是此时打开方式要以二进制方式打开 z_file=open(z_name,'rb') data=z_file.read() z_file.close() os.remove(z_file.name) response=HttpResponse(data,content_type='application/zip') fromurllibimportparse response['Content-Disposition']='attachment;filename='+parse.quote(z_name) returnresponse exceptExceptionase: logging.error(e) print(e)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。