2011-02-28 6 views
17

첨부 파일 다운로드로 csv 파일을 스트리밍하려고합니다. CSV 파일의 크기가 4MB 이상으로 늘어나고 있으며 모든 데이터가 만들어지고 메모리에 먼저 전달되기를 기다리지 않고 사용자가 파일을 적극적으로 다운로드 할 수있는 방법이 필요합니다.Django에서 CSV 파일 스트리밍

처음에는 장고의 FileWrapper 클래스를 기반으로하는 파일 래퍼를 사용했습니다. 그건 실패했다. 그 때 나는 응답 스트림 발전기를 사용하여 여기하는 방법을 보았다 : 나는 발전기 내에서 오류가 발생하면 How to stream an HttpResponse with Django

, 나는 내가 get_row_data() 기능을 적절한 데이터를 생성하고 볼 수 있지만를 내가하려고 할 때 비어있는 응답을 반환하십시오. 또한 장고를 비활성화했습니다 GZipMiddleware. 아무도 내가 뭘 잘못하고 있는지 알아?

편집 : 문제는 ConditionalGetMiddleware입니다. 나는 그것을 대체해야했다, 코드는 아래 답변에있다. 여기

from django.views.decorators.http import condition @condition(etag_func=None) def csv_view(request, app_label, model_name): """ Based on the filters in the query, return a csv file for the given model """ #Get the model model = models.get_model(app_label, model_name) #if there are filters in the query if request.method == 'GET': #if the query is not empty if request.META['QUERY_STRING'] != None: keyword_arg_dict = {} for key, value in request.GET.items(): #get the query filters keyword_arg_dict[str(key)] = str(value) #generate a list of row objects, based on the filters objects_list = model.objects.filter(**keyword_arg_dict) else: #get all the model's objects objects_list = model.objects.all() else: #get all the model's objects objects_list = model.objects.all() #create the reponse object with a csv mimetype response = HttpResponse( stream_response_generator(model, objects_list), mimetype='text/plain', ) response['Content-Disposition'] = "attachment; filename=foo.csv" return response 

내가 응답 스트림에 사용하는 발전기입니다 :

def get_row_data(model, row): 
    """Get a row of csv data from an object""" 
    #Create a temporary csv handle 
    csv_handle = cStringIO.StringIO() 
    #create the csv output object 
    csv_output = csv.writer(csv_handle) 
    value_list = [] 
    for field in model._meta.fields: 
     #if the field is a related field (ForeignKey, ManyToMany, OneToOne) 
     if isinstance(field, RelatedField): 
      #get the related model from the field object 
      related_model = field.rel.to 
      for key in row.__dict__.keys(): 
       #find the field in the row that matches the related field 
       if key.startswith(field.name): 
        #Get the unicode version of the row in the related model, based on the id 
        try: 
         entry = related_model.objects.get(
          id__exact=int(row.__dict__[key]), 
          ) 
        except: 
         pass 
        else: 
         value = entry.__unicode__().encode("utf-8") 
         break 
     #if it isn't a related field 
     else: 
      #get the value of the field 
      if isinstance(row.__dict__[field.name], basestring): 
       value = row.__dict__[field.name].encode("utf-8") 
      else: 
       value = row.__dict__[field.name] 
     value_list.append(value) 
    #add the row of csv values to the csv file 
    csv_output.writerow(value_list) 
    #Return the string value of the csv output 
    return csv_handle.getvalue() 
: 여기

def stream_response_generator(model, objects_list): 
    """Streaming function to return data iteratively """ 
    for row_item in objects_list: 
     yield get_row_data(model, row_item) 
     time.sleep(1) 

을 그리고 난 CSV 행 데이터를 생성하는 방법을 여기

은이다

답변

30

다음은 CSV를 스트리밍하는 간단한 코드입니다. 당신은 아마이에서 당신이해야 할 무엇에 갈 수

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 

이 단순히 행을 읽고 그것을 산출, 메모리 내 파일에 각 행을 기록합니다.

이 버전은 대량의 데이터를 생성하기위한 더 효율적이지만, 사용하기 전에 위를 이해해야합니다 : 내가 가졌다

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

나는이 답변을 정말 좋아하지만 이것이 내 문제가 아니라는 사실이 밝혀졌습니다. 당신이 쓴이 정확한 코드를 문자 그대로 사용했습니다. 단지 응답을 생성 할 것인지를보기 위해서였습니다. 그러나 응답은 0 바이트로 돌아옵니다. 그래서 저는 여전히 같은 결과에 갇혀 있습니다. – bfrederix

+0

이 코드는 제대로 작동하므로 문제 해결에 필요한 환경에 문제가 있습니다. –

2

문제는 ConditionalGetMiddleware로했다. 이 질문에 현명한 대답에서 (빌린 코드를

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) 

그럼 당신은 당신의 ConditionalMiddlewareCompatProxy 미들웨어와 ConditionalGetMiddleware를 대체하고보기에 : 나는 장고 - 피스톤 스트리밍 수있는 ConditionalGetMiddleware에 대한 대체 미들웨어 생각해 보았다)

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 
11

미들웨어 문제 장고 1.5로 해결되었다 및 StreamingHttpResponse가 도입되었다. 다음은 수행해야이 how to output csv from Django에 대한 몇 가지 문서입니다하지만 StreamingHttpResponse 그래서 난 앞으로 및 opened a ticket in order to track it 갔다으로는 이용하지 않습니다

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 

.