zoukankan      html  css  js  c++  java
  • Django文件下载2

    django提供文件下载时,若果文件较小,解决办法是先将要传送的内容全生成在内存中,然后再一次性传入Response对象中:

    1
    2
    3
    4
    def simple_file_download(request):
        # do something...
        content = open("simplefile""rb").read()
        return HttpResponse(content)

    如果文件非常大时,最简单的办法就是使用静态文件服务器,比如Apache或者Nginx服务器来处理下载。不过有时候,我们需要对用户的权限做一下限定,或者不想向用户暴露文件的真实地址,或者这个大内容是临时生成的(比如临时将多个文件合并而成的),这时就不能使用静态文件服务器了。

    django文档中提到,可以向HttpResponse传递一个迭代器,流式的向客户端传递数据。

    要自己写迭代器的话,可以用yield:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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

    或者使用生成器表达式,下面是django文档中提供csv大文件下载的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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也提供一个文件包装器,将类文件对象包装成一个迭代器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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

    使用时:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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对流数据进行处理。

    压缩为zip文件下载:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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来处理大文件下载都不是一个很好的注意,最好的办法是django做权限判断,然后让静态服务器处理下载。

    这需要使用sendfile的机制:"传统的Web服务器在处理文件下载的时候,总是先读入文件内容到应用程序内存,然后再把内存当中的内容发送给客户端浏览器。这种方式在应付当今大负载网站会消耗更多的服务器资源。sendfile是现代操作系统支持的一种高性能网络IO方式,操作系统内核的sendfile调用可以将文件内容直接推送到网卡的buffer当中,从而避免了Web服务器读写文件的开销,实现了“零拷贝”模式。 "

    Apache服务器里需要mod_xsendfile模块来实现,而Nginx是通过称为X-Accel-Redirect的特性来实现。

    nginx配置文件:

    1
    2
    3
    4
    5
    6
    # Will serve /var/www/files/myfile.tar.gz
    # When passed URI /protected_files/myfile.tar.gz
    location /protected_files {
        internal;
        alias /var/www/files;
    }

    或者

    1
    2
    3
    4
    5
    6
    # 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的区别。

    django中:

    1
    response['X-Accel-Redirect']='/protected_files/%s'%filename

    这样当向django view函数发起request时,django负责对用户权限进行判断或者做些其它事情,然后向nginx转发url为/protected_files/filename的请求,nginx服务器负责文件/var/www/protected_files/filename的下载:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @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
  • 相关阅读:
    POJ 3268 Silver Cow Party (Dijkstra)
    怒学三算法 POJ 2387 Til the Cows Come Home (Bellman_Ford || Dijkstra || SPFA)
    CF Amr and Music (贪心)
    CF Amr and Pins (数学)
    POJ 3253 Fence Repair (贪心)
    POJ 3069 Saruman's Army(贪心)
    POJ 3617 Best Cow Line (贪心)
    CF Anya and Ghosts (贪心)
    CF Fox And Names (拓扑排序)
    mysql8.0的新特性
  • 原文地址:https://www.cnblogs.com/taoyucheng/p/10474627.html
Copyright © 2011-2022 走看看