Скачать несколько файлов одним архивом - PullRequest
0 голосов
/ 27 марта 2019

У меня есть несколько изображений в корневой папке проекта, и я хочу сделать их доступными для динамической загрузки в виде zip-архива.

Все изображения имеют одинаковое имя, но разница в порядковом номере в конце, поэтому япопытался сделать это

def zip_files(name, iterat):
    temp = tempfile.TemporaryFile()
    archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
    for index in range(iterat):
        filename = name+"_"+str(index)+".jpg"
        archive.write(filename)
    archive.close()
    wrapper = FileWrapper(temp)
    response = HttpResponse(wrapper, content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=test.zip'
    response['Content-Length'] = temp.tell()
    temp.seek(0)
    return response

, поэтому я получил ошибки в строках response['Content-Length'] = temp.tell() и temp.seek(0)

Операция в закрытом файле.

икогда я комментирую эти строки, возвращаемые данные в ajax пустые (потому что это запускается как запрос ajax)

Update

Я использовал NamedTemporaryFile какследующее:

def zip_files(name, iterat):
    temp = tempfile.NamedTemporaryFile(delete=False)
    archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED)
    for index in range(iterat):
        filename = name+"_"+str(index)+".jpg"
        archive.write(filename)
    archive.close()
    wrapper = FileWrapper(temp)
    response = HttpResponse(wrapper, content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=test.zip'
    archive = zipfile.ZipFile(temp.name, 'r')
    response['Content-Length'] = open(temp.name).tell()
    return response

теперь у меня нет ошибок на стороне сервера, но возвращенные данные в запрос ajax все еще пустые, на вкладке сети браузера вся информация, добавленная к HttpResponse, находится в заголовках ответа следующим образом:

Content-Disposition: attachment; filename=test.zip
Content-Length: 0
Content-Type: application/zip
Date: Wed, 27 Mar 2019 15:32:08 GMT
Server: WSGIServer/0.2 CPython/3.7.2
X-Frame-Options: SAMEORIGIN

1 Ответ

1 голос
/ 27 марта 2019

Вызов tempfile.TemporaryFile() возвращает дескриптор файла, а не имя файла.

Это закроет дескриптор файла:

 archive.close()

После этого ручку больше нельзя использовать. Фактически, файл будет удален с диска, закрыв его: https://docs.python.org/3/library/tempfile.html#tempfile.TemporaryFile

Так что даже если бы вы могли запросить имя tempfile.TemporaryFile() для его имени, это не помогло бы.

Что вам нужно сделать, это попросить временное имя файла (а не только файл). Затем создайте дескриптор для этого имени файла, запишите данные, закройте дескриптор. Для запроса создайте новый дескриптор файла, используя имя.

Метод tempfile.NamedTemporaryFile() должен работать для вас. Обязательно передайте опцию delete=False. Вы можете получить путь к файлу от temp.name. См https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile

Это оставит файл на диске после того, как ответ будет отправлен. Чтобы это исправить, расширьте FileWrapper и перезапишите close() метод:

 class DeletingFileWrapper(FileWrapper):
     def close(self):
         # First close the file handle to avoid errors when deleting the file
         super(DeletingFileWrapper,self).close()

         os.remove(self.filelike.name)

Если ZIP-файл большой, вы также должны использовать StreamingHttpResponse вместо HttpResponse, так как последний будет считывать весь файл сразу в память.

Обновление

Вы все еще используете недопустимый (закрытый) дескриптор файла здесь: FileWrapper(temp)

Правильный код будет:

wrapper = DeletingFileWrapper(open(temp.name, 'b'))

И вам нужно использовать метод, который принимает имя файла для определения длины, потому что open(temp.name).tell() всегда возвращает 0. Проверьте модуль os.

Смотри также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...