72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
from django.http import StreamingHttpResponse
|
|
|
|
import zlib
|
|
import datetime
|
|
import os
|
|
import anyio
|
|
from stat import S_IFREG
|
|
from stream_zip import ZIP_64, stream_zip, async_stream_zip
|
|
from channels.layers import get_channel_layer
|
|
from asgiref.sync import async_to_sync
|
|
|
|
|
|
def send_sync_channel_message(channel_name, context, data):
|
|
async_to_sync(get_channel_layer().group_send)(channel_name, {
|
|
"type": context,
|
|
"data": data
|
|
})
|
|
|
|
|
|
class StreamingZipFileResponse(StreamingHttpResponse):
|
|
# https://stream-zip.docs.trade.gov.uk/
|
|
# https://github.com/sandes/zipfly/tree/master
|
|
def __init__(self, filename, file_list, compression_level=0, is_async=False, *args, **kwargs):
|
|
self.file_list = file_list
|
|
super().__init__(content_type='application/octet-stream', *args, **kwargs)
|
|
self['Content-Disposition'] = f'attachment; filename="{filename}"'
|
|
# self['Cache-Control'] = "no-cache"
|
|
# self['X-Accel-Buffering'] = "no"
|
|
|
|
self.zipped = None
|
|
|
|
if is_async:
|
|
self.zipped = async_stream_zip(
|
|
self._async_local_files(),
|
|
get_compressobj=lambda: zlib.compressobj(wbits=-zlib.MAX_WBITS, level=compression_level)
|
|
)
|
|
else:
|
|
self.zipped = stream_zip(
|
|
self._sync_local_files()
|
|
)
|
|
self.streaming_content = self.zipped
|
|
|
|
def _get_total_length(self):
|
|
return sum([os.stat(pathname).st_size for pathname, _ in self.file_list])
|
|
|
|
def _sync_local_files(self):
|
|
now = datetime.datetime.now()
|
|
|
|
def contents(path):
|
|
with open(path, "rb") as f:
|
|
while chunk := f.read(64 * 1024):
|
|
yield chunk
|
|
|
|
return (
|
|
(dest, now, S_IFREG | 0o600, ZIP_64, contents(pathname))
|
|
for pathname, dest in self.file_list
|
|
)
|
|
|
|
async def _async_local_files(self):
|
|
now = datetime.datetime.now()
|
|
|
|
async def contents(path):
|
|
try:
|
|
async with await anyio.open_file(path, "rb") as f:
|
|
while chunk := await f.read(64 * 1024):
|
|
yield chunk
|
|
except RuntimeError as e:
|
|
print("Event loop not running, maybe cancel by user ?")
|
|
raise e
|
|
|
|
for pathname, dest in self.file_list:
|
|
yield dest, now, S_IFREG | 0o600, ZIP_64, contents(pathname) |