Merge pull request #1527 from pierotofy/stream

Use StreamingHttpResponse when downloading zip streams
pull/1533/head v2.5.4
Piero Toffanin 2024-07-18 15:57:45 -04:00 zatwierdzone przez GitHub
commit b619a007af
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
4 zmienionych plików z 20 dodań i 10 usunięć

Wyświetl plik

@ -11,6 +11,8 @@ from django.core.files.uploadedfile import InMemoryUploadedFile
from django.db import transaction from django.db import transaction
from django.http import FileResponse from django.http import FileResponse
from django.http import HttpResponse from django.http import HttpResponse
from django.http import StreamingHttpResponse
from app.vendor import zipfly
from rest_framework import status, serializers, viewsets, filters, exceptions, permissions, parsers from rest_framework import status, serializers, viewsets, filters, exceptions, permissions, parsers
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
@ -340,8 +342,13 @@ def download_file_response(request, filePath, content_disposition, download_file
def download_file_stream(request, stream, content_disposition, download_filename=None): def download_file_stream(request, stream, content_disposition, download_filename=None):
response = HttpResponse(FileWrapper(stream), if isinstance(stream, zipfly.ZipStream):
content_type=(mimetypes.guess_type(download_filename)[0] or "application/zip")) f = stream.generator()
else:
# This should never happen, but just in case..
raise exceptions.ValidationError("stream not a zipstream instance")
response = StreamingHttpResponse(f, content_type=(mimetypes.guess_type(download_filename)[0] or "application/zip"))
response['Content-Type'] = mimetypes.guess_type(download_filename)[0] or "application/zip" response['Content-Type'] = mimetypes.guess_type(download_filename)[0] or "application/zip"
response['Content-Disposition'] = "{}; filename={}".format(content_disposition, download_filename) response['Content-Disposition'] = "{}; filename={}".format(content_disposition, download_filename)

Wyświetl plik

@ -74,7 +74,7 @@ class TestApiTask(BootTransactionTestCase):
assets_path = os.path.join(settings.MEDIA_TMP, "all.zip") assets_path = os.path.join(settings.MEDIA_TMP, "all.zip")
with open(assets_path, 'wb') as f: with open(assets_path, 'wb') as f:
f.write(res.content) f.write(b''.join(res.streaming_content))
remove_perm('change_project', user, project) remove_perm('change_project', user, project)
@ -272,7 +272,7 @@ class TestApiTask(BootTransactionTestCase):
assets_path = os.path.join(settings.MEDIA_TMP, "backup.zip") assets_path = os.path.join(settings.MEDIA_TMP, "backup.zip")
with open(assets_path, 'wb') as f: with open(assets_path, 'wb') as f:
f.write(res.content) f.write(b''.join(res.streaming_content))
assets_file = open(assets_path, 'rb') assets_file = open(assets_path, 'rb')

13
app/vendor/zipfly.py vendored
Wyświetl plik

@ -46,7 +46,6 @@ class ZipflyStream(io.RawIOBase):
def size(self): def size(self):
return self._size return self._size
class ZipFly: class ZipFly:
def __init__(self, def __init__(self,
@ -280,13 +279,17 @@ class ZipFly:
class ZipStream: class ZipStream:
def __init__(self, paths): def __init__(self, paths):
self.paths = paths self.paths = paths
self.generator = None self._generator = None
def lazy_load(self, chunksize): def lazy_load(self, chunksize):
if self.generator is None: if self._generator is None:
zfly = ZipFly(paths=self.paths, mode='w', chunksize=chunksize) zfly = ZipFly(paths=self.paths, mode='w', chunksize=chunksize)
self.generator = zfly.generator() self._generator = zfly.generator()
def read(self, count): def read(self, count):
self.lazy_load(count) self.lazy_load(count)
return next(self.generator) return next(self._generator)
def generator(self):
self.lazy_load(0x8000)
return self._generator

Wyświetl plik

@ -1,6 +1,6 @@
{ {
"name": "WebODM", "name": "WebODM",
"version": "2.5.3", "version": "2.5.4",
"description": "User-friendly, extendable application and API for processing aerial imagery.", "description": "User-friendly, extendable application and API for processing aerial imagery.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {