Home  >  Article  >  Web Front-end  >  How to implement file download function in Django

How to implement file download function in Django

php中世界最好的语言
php中世界最好的语言Original
2018-04-11 17:17:142842browse

This time I will show you how to implement the file download function in Django. What are the precautions for Django to implement the file download function. The following is a practical case. Let’s take a look. one time.

If a website built based on Django provides a file download function, the easiest way is to hand over the static files to Nginx for processing. However, sometimes, due to the logic of the website itself, it is necessary to provide the download function through Django, such as Page data export function (download dynamically generated files), check user permissions before downloading files, etc. Therefore, it is necessary to study the implementation of the file download function in Django.

The simplest implementation of file download function

Just put the file stream into the HttpResponse object, such as:

def file_download(request):
  # do something...
  with open('file_name.txt') as f:
    c = f.read()
  return HttpResponse(c)

This method is simple and crude, suitable for downloading small files, but if the file is very large, this method will occupy a lot of memory and even cause the server to crash

More reasonable file download function

Django's HttpResponse object allows iterators to be passed in as parameters. By replacing the incoming parameter c in the above code with an iterator, the above download function can be optimized to be suitable for both large and small files; and Django goes one step further and recommends using StreamingHttpResponse Object replaces the HttpResponse object. The StreamingHttpResponse object is used to send a file stream to the browser. It is very similar to the HttpResponse object. For the file download function, it is more reasonable to use the StreamingHttpResponse object.

Therefore, for a more reasonable file download function, you should first write an iterator for processing files, and then pass this iterator as a parameter to the StreaminghttpResponse object, such as:

from django.http import StreamingHttpResponse
def big_file_download(request):
  # do something...
 
  def file_iterator(file_name, chunk_size=512):
    with open(file_name) as f:
      while True:
        c = f.read(chunk_size)
        if c:
          yield c
        else:
          break
 
  the_file_name = "file_name.txt"
  response = StreamingHttpResponse(file_iterator(the_file_name))
 
  return response

The file download function is optimized again

The above code has completed transferring the files on the server to the browser through file streaming. However, the file stream is usually displayed in the browser in garbled form instead of being downloaded to the hard disk. Therefore, some optimizations need to be done. Let the file stream be written to the hard disk. Optimization is very simple. Just assign the following values ​​to the Content-Type and Content-Disposition fields of the StreamingHttpResponse object, such as:

response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="test.pdf"'

The complete code is as follows:

from django.http import StreamingHttpResponse
def big_file_download(request):
  # do something... 
  def file_iterator(file_name, chunk_size=512):
    with open(file_name) as f:
      while True:
        c = f.read(chunk_size)
        if c:
          yield c
        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) 
  return response

Specific export file format

Export Excel table

1. First, export the Excel table directly

First get the data to be exported and save it in list form.

The data is then written to Excel and returned to the page for download in a streaming manner.

import xlwt
import io
import json
from django.http import HttpResponse
def set_style(name, height, bold=False):
  style = xlwt.XFStyle() # 初始化样式
  font = xlwt.Font() # 为样式创建字体
  font.name = name # 'Times New Roman'
  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
  return style
def write_excel(data, name, header):
  # 打开一个Excel工作簿
  file = xlwt.Workbook()
  # 新建一个sheet,如果对一个单元格重复操作,会引发异常,所以加上参数cell_overwrite_ok=True
  table = file.add_sheet(name, cell_overwrite_ok=True)
  if data is None:
    return file
  # 写标题栏
  row0 = [u'业务', u'状态', u'北京', u'上海', u'广州', u'深圳', u'状态小计']
  for i in range(0, len(row0)):
    table.write_merge(0, 0, i, i, row0[i], set_style('Times New Roman', 220, True))
  table.write_merge(0, 2, 7, 9, "单元格合并", set_style('Times New Roman', 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)
  # 写入数据
  for line in data:
    for i in range(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')
  from urllib import parse
  response['Content-Disposition'] = 'attachment;filename=' + parse.quote("excel_name") + '.xls'
  # 将文件流写入到response返回
  response.write(res)
  return response
def get_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()
  return res

2. Export json file

Exporting json files is not as troublesome as Excel. You only need to splice json format data. It is still very simple to export directly to the local. But when exporting to a web page, how can you directly return the stream without saving it to the local like exporting excel?

def write_json(data):
  try:
    json_stream = get_json_stream(data)
    response = HttpResponse(content_type='application/json')
    from urllib import parse
    response['Content-Disposition'] = 'attachment;filename=' + parse.quote("test") + '.json'
    response.write(json_stream)
    return response
  except Exception as e:
    print(e)
def get_json_stream(data):
  # 开始这里我用ByteIO流总是出错,但是后来参考廖雪峰网站用StringIO就没问题
  file = io.StringIO()
  data = json.dumps(data)
  file.write(data)
  res = file.getvalue()
  file.close()
  return res

3. Export compressed package

Since exporting two files cannot return both at the same time, consider putting the two files into a package and then returning the package as a stream.

think? What is exported at this time is a zip package. How do I write these two file streams into the zip package? It seems a bit unreasonable. Later, under the guidance of the boss, the files to be packaged were saved locally, and after being packaged into zip, the local files were deleted, and then the zip file stream was read, written to the response, and returned to the zip file stream.

def write_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')
    from urllib import parse
    response['Content-Disposition'] = 'attachment;filename=' + parse.quote(z_name)
    return response
  except Exception as e:
    logging.error(e)
    print(e)

I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!

Recommended reading:

Detailed explanation of the steps for vue to use the xe-utils function library

vue2.0 routing is not displayed How to handle router-view

How does Native use fetch to implement the image upload function

The above is the detailed content of How to implement file download function in Django. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn