下载大文件:Django 上传 / 下载 大文件
DjangoWeb 下载
django中实现下载功能的方法还是比较多,当我们使用视图函数的时候:
项目需求
需求分析
- 单个下载文件
- 打包下载多个文件
实现逻辑
- 对于不同的请求,分开处理
- 单个文件:/download/?id=1&type=img
- 打包文件:/download/id=1&type=all
代码实现
压缩文件
1. zipstream
import os
import zipstream
class ZipUtilities(object):
    """
    将文件或者文件夹打包成ZIP格式的文件,然后下载,在后台可以通过response完成下载
    """
    zip_file = None
    def __init__(self):
        self.zip_file = zipstream.ZipFile(mode='w', compression=zipstream.ZIP_DEFLATED)
    def to_zip(self, file, name):
        if os.path.isfile(file):
            self.zip_file.write(file, arcname=os.path.basename(file))
        else:
            self.add_folder_to_zip(file, name)
    def add_folder_to_zip(self, folder, name):
        for file in os.listdir(folder):
            full_path = os.path.join(folder, file)
            if os.path.isfile(full_path):
                self.zip_file.write(
                    full_path,
                    arcname=os.path.join(name, os.path.basename(full_path))
                )
            elif os.path.isdir(full_path):
                self.add_folder_to_zip(
                    full_path,
                    os.path.join(name, os.path.basename(full_path))
                )
    def close(self):
        if self.zip_file:
            self.zip_file.close()
2. zipfile
import zipfile
from io import StringIO
class MemoryZipFile(object):
    def __init__(self):
        # 创建内存文件
        self._memory_zip = StringIO()
    def append_file(self, filename_in_zip, local_file_full_path):
        """
        description:写文件内容到zip
        注意这里的第二个参数是本地磁盘文件的全路径:
            windows: c:/demo/1.jpg
            linux: /usr/local/test/1.jpg
        """
        zf = zipfile.ZipFile(self._memory_zip, "a", zipfile.ZIP_DEFLATED, False)
        zf.write(local_file_full_path, filename_in_zip)
        for zfile in zf.filelist: zfile.create_system = 0
        return self
    def read(self):
        """
        description: 读取zip文件内容
        """
        self._memory_zip.seek(0)
        return self._memory_zip.read()
下载文件
这里是使用
第一种(zipstream)方式返回数据的
def download(request):
    """
    从前端接受请求
    ==> 使用内存读取的方式
    ==> 完成单个数据下载,或者是多个文件打包下载
    """
    file_id = request.GET.get("id", None)
    download_type = request.GET.get("type", None)
    file = A.objects.filter(id=file_id).first()
    if file:
        if download_type == "all":
            # 通过内存的方式打包下载文件
            utilities = zip_file.ZipUtilities()
            folder_path = re.findall(r'(.*/).*', file.img)[0]
            folder_name = re.findall(r'.*/A/(.*)/.*', file.img)[0]
            utilities.add_folder_to_zip(folder_path, folder_name)
            # utilities.close() # TODO: 这里关闭内存的话,数据没法返回
            response = StreamingHttpResponse(
                utilities.zip_file, 
                content_type='application/zip'
            )
            response['Content-Disposition'] = \
                'attachment;filename="{0}.zip"'.format(folder_name)
            return response
        else:
            file_path = getattr(file, download_type)
            file_name = re.findall(r'.*/(.*)', file_path)[0]
            def file_iterator(file_path, chunk_size=512):
                with open(file_path, mode='rb') as f:
                    while True:
                        count = f.read(chunk_size)
                        if count:
                            yield count
                        else:
                            break
            try:
                response = StreamingHttpResponse(file_iterator(file_path))
                response['Content-Type'] = 'application/octet-stream'
                response['Content-Disposition'] = \
                    'attachment;filename="%s"' % (urlquote(file_name))
            except:
                return HttpResponse("Sorry but Not Found the File")
            return response
    return HttpResponse("文件下载失败")
 
             
           
             
                         
             
            
评论区