Streaming a CSV file in Django

后端 未结 3 1688
感动是毒
感动是毒 2020-12-08 05:15

I am attempting to stream a csv file as an attachment download. The CSV files are getting to be 4MB in size or more, and I need a way for the user to actively download the f

相关标签:
3条回答
  • 2020-12-08 05:55

    The middleware issue has been solved as of Django 1.5 and a StreamingHttpResponse has been introduced. The following should do:

    import cStringIO as StringIO
    import csv
    
    def csv_view(request):
        ...
        # Assume `rows` is an iterator or lists
        def stream():
            buffer_ = StringIO.StringIO()
            writer = csv.writer(buffer_)
            for row in rows:
                writer.writerow(row)
                buffer_.seek(0)
                data = buffer_.read()
                buffer_.seek(0)
                buffer_.truncate()
                yield data
        response = StreamingHttpResponse(
            stream(), content_type='text/csv'
        )
        disposition = "attachment; filename=file.csv"
        response['Content-Disposition'] = disposition
        return response
    

    There's some documentation on how to output csv from Django but it doesn't take advantage of the StreamingHttpResponse so I went ahead and opened a ticket in order to track it.

    0 讨论(0)
  • 2020-12-08 05:57

    Here's some simple code that'll stream a CSV; you can probably go from this to whatever you need to do:

    import cStringIO as StringIO
    import csv
    
    def csv(request):
        def data():
            for i in xrange(10):
                csvfile = StringIO.StringIO()
                csvwriter = csv.writer(csvfile)
                csvwriter.writerow([i,"a","b","c"])
                yield csvfile.getvalue()
    
        response = HttpResponse(data(), mimetype="text/csv")
        response["Content-Disposition"] = "attachment; filename=test.csv"
        return response
    

    This simply writes each row to an in-memory file, reads the row and yields it.

    This version is more efficient for generating bulk data, but be sure to understand the above before using it:

    import cStringIO as StringIO
    import csv
    
    def csv(request):
        csvfile = StringIO.StringIO()
        csvwriter = csv.writer(csvfile)
    
        def read_and_flush():
            csvfile.seek(0)
            data = csvfile.read()
            csvfile.seek(0)
            csvfile.truncate()
            return data
    
        def data():
            for i in xrange(10):
                csvwriter.writerow([i,"a","b","c"])
            data = read_and_flush()
            yield data
    
        response = HttpResponse(data(), mimetype="text/csv")
        response["Content-Disposition"] = "attachment; filename=test.csv"
        return response
    
    0 讨论(0)
  • 2020-12-08 05:59

    The problem I was having was with the ConditionalGetMiddleware. I saw django-piston come up with a replacement middleware for the ConditionalGetMiddleware that allows streaming:

    from django.middleware.http import ConditionalGetMiddleware
    
    def compat_middleware_factory(klass):
        """
        Class wrapper that only executes `process_response`
        if `streaming` is not set on the `HttpResponse` object.
        Django has a bad habbit of looking at the content,
        which will prematurely exhaust the data source if we're
        using generators or buffers.
        """
        class compatwrapper(klass):
            def process_response(self, req, resp):
                if not hasattr(resp, 'streaming'):
                    return klass.process_response(self, req, resp)
                return resp
        return compatwrapper
    
    ConditionalMiddlewareCompatProxy = compat_middleware_factory(ConditionalGetMiddleware)
    

    So then you will replace ConditionalGetMiddleware with your ConditionalMiddlewareCompatProxy middleware, and in your view (borrowed code from a clever answer to this question):

    def csv_view(request):
        def data():
            for i in xrange(10):
                csvfile = StringIO.StringIO()
                csvwriter = csv.writer(csvfile)
                csvwriter.writerow([i,"a","b","c"])
                yield csvfile.getvalue()
    
        #create the reponse object with a csv mimetype
        response = HttpResponse(
            data(),
            mimetype='text/csv',
            )
        #Set the response as an attachment with a filename
        response['Content-Disposition'] = "attachment; filename=test.csv"
        response.streaming = True
        return response
    
    0 讨论(0)
提交回复
热议问题