Django - 动态创建的gzip文件的HTTP流

0 投票
1 回答
1082 浏览
提问于 2025-04-18 17:39

我使用了StringIO来创建一个可以存放xml数据的文件对象,然后用这个文件对象创建了一个gzip文件。现在我在用django通过HTTP发送这个文件时遇到了问题,因为文件大小不固定,有时候可能会很大。这就是我选择使用HTTPStream而不是普通的HTTP响应的原因。我也搞不清楚怎么发送文件的长度,因为这个文件对象是不可寻址的。

谢谢你的帮助,祝好!

这是我的代码:

# Get the XML string
xml_string = get_xml_cebd(cebds)

# Write the xml string to the string io holder
cebd_xml_file.write(xml_string)

# Flush the xml file buffer
cebd_xml_file.flush()

# Create the Gzip file handler, with the StringIO in the fileobj
gzip_handler = gzip.GzipFile(
    fileobj=cebd_xml_file,
    filename=base_file_name + '.xml',
    mode='wb'
)

# Write the XML data into the gziped file
gzip_handler.write(cebd_xml_file.getvalue())
gzip_handler.flush()

# Generate the response using the file warpper, content type: x-gzip
# Since this file can be big, best to use the StreamingHTTPResponse
# that way we can garantee that the file will be sent entirely
response = StreamingHttpResponse(
    gzip_handler,
    content_type='application/x-gzip'
)

# Add content disposition so the browser will download the file
response['Content-Disposition'] = ('attachment; filename=' +
    base_file_name + '.xml.gz')

# Content size
gzip_handler.seek(0, os.SEEK_END)
response['Content-Length'] = gzip_handler.tell()

1 个回答

1

我找到了两个问题的解决办法。首先,应该传给HTTPStream的处理器是StringIO,而不是Gzip处理器。StringIO处理器是可以定位的,这样我就可以在数据经过gzip压缩后检查它的大小。还有一个小技巧是,要调用gzip处理器的close方法,这样它才能把crc32和大小信息加到gzip文件里,否则发送的数据大小会是0。对于StringIO处理器,不要调用close方法,因为HTTPStream需要保持它打开,以便继续传输数据,垃圾回收器会在流结束后自动关闭它。

这是最终的代码:

# Use cStringIO to create the file on the fly
cebd_xml_file = StringIO.StringIO()

# Create the file name ...
base_file_name = "cebd"

# Get the XML String
xml_string = get_xml_cebd(cebds)

# Create the Gzip file handler, with the StringIO in the fileobj
gzip_handler = gzip.GzipFile(
    fileobj=cebd_xml_file,
    filename=base_file_name + '.xml',
    mode='wb'
)

# Write the XML data into the gziped file
gzip_handler.write(xml_string)

# Flush the data
gzip_handler.flush()

# Close the Gzip handler, the close method will add the CRC32 and the size
gzip_handler.close()

# Generate the response using the file warpper, content type: x-gzip
# Since this file can be big, best to use the StreamingHTTPResponse
# that way we can garantee that the file will be sent entirely
response = StreamingHttpResponse(
    cebd_xml_file.getvalue(),
    content_type='application/x-gzip'
)

# Add content disposition so the browser will download the file, don't use mime type !
response['Content-Disposition'] = ('attachment; filename=' +
    base_file_name + '.xml.gz')

# Content size
cebd_xml_file.seek(0, os.SEEK_END)
response['Content-Length'] = cebd_xml_file.tell()

# Send back the response to the request, don't close the StringIO handler !
return response

谢谢,希望这能帮助到其他人。

撰写回答