Как установить разрешения (атрибуты) для файла в ZIP-файле с помощью модуля Python zipfile?

Когда я извлекаю файлы из ZIP-файла, созданного с помощью модуля Python zipfile, все файлы недоступны для записи, только для чтения и т. д.

Файл создается и распаковывается под Linux и Python 2.5.2.

Насколько я могу судить, мне нужно установить свойство ZipInfo.external_attr для каждого файла, но, похоже, это нигде не задокументировано, может ли кто-нибудь просветить меня?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
45
0
25 463
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Посмотри на это: Установить права доступа к сжатому файлу в Python

Я не совсем уверен, что вы этого хотите, но похоже, что это так.

Ключевая линия выглядит так:

zi.external_attr = 0777 << 16L

Похоже, он там устанавливает права для 0777.

Ответ принят как подходящий

Кажется, это работает (спасибо Эвану, поместив его здесь, чтобы строка находилась в контексте):

buffer = "path/filename.zip"  # zip filename to write (or file-like object)
name = "folder/data.txt"      # name of file inside zip 
bytes = "blah blah blah"      # contents of file inside zip

zip = zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo(name)
info.external_attr = 0777 << 16L # give full access to included file
zip.writestr(info, bytes)
zip.close()

Я все еще хотел бы увидеть что-то, что документирует это ... Дополнительный ресурс, который я нашел, это примечание о формате Zip-файла: http://www.pkware.com/documents/casestudies/APPNOTE.TXT

Это было бы более читабельно, если бы в примере были определены буфер, имя и байты.

Error 454 12.06.2013 23:37

конечно, добавил несколько примеров определений.

Tom 14.06.2013 03:28

Для python 3 вы должны написать этот 0o777 << 16

Clément 28.08.2015 05:30

Также: как написано, этот код пометит записываемый в архив файл как последний измененный в 1980 году; конструктор ZipInfo принимает дату последнего изменения в качестве другого конструктора.

Clément 08.10.2015 15:00

см. более новый ответ для более правильного решения stackoverflow.com/a/48435482/305975

underscorediscovery 25.01.2018 07:13

Похоже, более правильное решение отличается тем, что устанавливает дополнительный бит атрибута?

mwfearnley 01.10.2018 16:18

Когда вы делаете это так, нормально ли это работает?

zf = zipfile.ZipFile("something.zip")
for name in zf.namelist():
    f = open(name, 'wb')
    f.write(self.read(name))
    f.close()

Если нет, я бы предложил добавить os.chmod в цикл for с разрешениями 0777, например:

zf = zipfile.ZipFile("something.zip")
for name in zf.namelist():
    f = open(name, 'wb')
    f.write(self.read(name))
    f.close()
    os.chmod(name, 0777)

Я не использую Python для извлечения zip-архива, zip-архив создается веб-сервером и извлекается с использованием чего-то на компьютере пользователя. В моем случае программа-менеджер архива gnome.

Tom 12.01.2009 10:20

Эта ссылка содержит больше информации, чем что-либо еще, что мне удалось найти в сети. Даже в zip-архиве ничего нет. Копирование соответствующего раздела для потомков. Этот патч на самом деле не касается документирования этого формата, он просто показывает, насколько жалкой (читай несуществующей) является текущая документация.

# external_attr is 4 bytes in size. The high order two
# bytes represent UNIX permission and file type bits,
# while the low order two contain MS-DOS FAT file
# attributes, most notably bit 4 marking directories.
if node.isfile:
    zipinfo.compress_type = ZIP_DEFLATED
    zipinfo.external_attr = 0644 << 16L # permissions -r-wr--r--
    data = node.get_content().read()
    properties = node.get_properties()
    if 'svn:special' in properties and \
           data.startswith('link '):
        data = data[5:]
        zipinfo.external_attr |= 0120000 << 16L # symlink file type
        zipinfo.compress_type = ZIP_STORED
    if 'svn:executable' in properties:
        zipinfo.external_attr |= 0755 << 16L # -rwxr-xr-x
    zipfile.writestr(zipinfo, data)
elif node.isdir and path:
    if not zipinfo.filename.endswith('/'):
        zipinfo.filename += '/'
    zipinfo.compress_type = ZIP_STORED
    zipinfo.external_attr = 040755 << 16L # permissions drwxr-xr-x
    zipinfo.external_attr |= 0x10 # MS-DOS directory flag
    zipfile.writestr(zipinfo, '')

Также эта ссылка имеет следующее. Здесь младший байт предположительно означает самый правый (младший) байт из четырех байтов. Так что это для MS-DOS и, по-видимому, в противном случае может быть оставлен равным нулю.

external file attributes: (4 bytes)

      The mapping of the external attributes is
      host-system dependent (see 'version made by').  For
      MS-DOS, the low order byte is the MS-DOS directory
      attribute byte.  If input came from standard input, this
      field is set to zero.

Кроме того, исходный файл unix / unix.c в источниках для zip-программы InfoZIP, загруженный с Архивы Debian, содержит в комментариях следующее.

  /* lower-middle external-attribute byte (unused until now):
   *   high bit        => (have GMT mod/acc times) >>> NO LONGER USED! <<<
   *   second-high bit => have Unix UID/GID info
   * NOTE: The high bit was NEVER used in any official Info-ZIP release,
   *       but its future use should be avoided (if possible), since it
   *       was used as "GMT mod/acc times local extra field" flags in Zip beta
   *       versions 2.0j up to 2.0v, for about 1.5 years.
   */

Итак, если взять все это вместе, похоже, что на самом деле используется только второй старший байт, по крайней мере, для Unix.

Обновлено: Я спросил об аспекте Unix этого на Unix.SX в вопросе «Атрибут внешнего файла формата zip». Похоже, я ошибся в паре вещей. В частности, для Unix используются два верхних байта.

Некоторые константы в примере будут более разборчивыми при использовании констант из модуля stat (например, stat.S_IFLNK). Просматривая это, я обнаружил также unix.stackexchange.com/questions/14705/…

Epu 20.11.2013 00:48

@Epu Технически нет гарантии, что S_IFLNK будет равно 0120000 - как я уже упоминал, «значения Unix такие же, как в традиционных реализациях unix» и предоставил пример из одного, но точные числовые значения не гарантируются POSIX ( также не гарантируется, что S_IFLNK существует как константа), но 0120000 всегда означает символическую ссылку в контексте zip, поскольку это кроссплатформенный формат.

Random832 20.11.2013 01:33

Предыдущие ответы не помогли мне (в OS X 10.12). Я обнаружил, что помимо исполняемых флагов (восьмеричное число 755), мне также нужно установить флаг «обычный файл» (восьмеричный 100000). Я нашел это здесь: https://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute

Полный пример:

zipname = "test.zip"
filename = "test-executable"

zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)

f = open(filename, 'r')
bytes = f.read()
f.close()

info = zipfile.ZipInfo(filename)
info.date_time = time.localtime()
info.external_attr = 0100755 << 16L

zip.writestr(info, bytes, zipfile.ZIP_DEFLATED)

zip.close()

Полный пример моего конкретного варианта использования, создание zip-архива .app, чтобы все в папке Contents/MacOS/ было исполняемым: https://gist.github.com/Draknek/3ce889860cea4f59838386a79cc11a85

Также посмотрите, что делает Модуль zipfile Python:

def write(self, filename, arcname=None, compress_type=None):
    ...
    st = os.stat(filename)
    ...
    zinfo = ZipInfo(arcname, date_time)
    zinfo.external_attr = (st[0] & 0xFFFF) << 16L      # Unix attributes
    ...

``

Вы можете расширить класс ZipFile, чтобы изменить права доступа к файлу по умолчанию:

from zipfile import ZipFile, ZipInfo
import time

class PermissiveZipFile(ZipFile):
    def writestr(self, zinfo_or_arcname, data, compress_type=None):
        if not isinstance(zinfo_or_arcname, ZipInfo):
            zinfo = ZipInfo(filename=zinfo_or_arcname,
                            date_time=time.localtime(time.time())[:6])

            zinfo.compress_type = self.compression
            if zinfo.filename[-1] == '/':
                zinfo.external_attr = 0o40775 << 16   # drwxrwxr-x
                zinfo.external_attr |= 0x10           # MS-DOS directory flag
            else:
                zinfo.external_attr = 0o664 << 16     # ?rw-rw-r--
        else:
            zinfo = zinfo_or_arcname

        super(PermissiveZipFile, self).writestr(zinfo, data, compress_type)

Этот пример изменяет разрешение файла по умолчанию на 664 и сохраняет 775 для каталогов.

Связанный код:

  • ZipFile.writestr, Python 2.7
  • ZipFile.writestr, Python 3.6

Чтобы установить разрешения (атрибуты Unix) для файла в ZIP-файле с помощью модуля Python zipfile, передайте атрибуты как биты 16–31 атрибута external_attr ZipInfo.

Модуль zipfile Python принимает 16-битное поле "Mode" (в котором хранится поле st_mode из struct stat, содержащее права пользователя / группы / другие, setuid / setgid, информацию о символической ссылке и т. д.) Дополнительного блока ASi для Unix в битах external_attr вышеупомянутый.

Вы также можете импортировать модуль Python "stat", чтобы получить определения констант режима.

Вы также можете установить 3 в create_system, чтобы указать операционную систему, создавшую ZIP-архив: 3 = Unix; 0 = Windows.

Вот пример:

#!/usr/bin/python

import stat
import zipfile

def create_zip_with_symlink(output_zip_filename, link_source, link_target):
    zipInfo  = zipfile.ZipInfo(link_source)
    zipInfo.create_system = 3 
    unix_st_mode = stat.S_IFLNK | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH
    zipInfo.external_attr = unix_st_mode << 16 
    zipOut = zipfile.ZipFile(output_zip_filename, 'w', compression=zipfile.ZIP_DEFLATED)
    zipOut.writestr(zipInfo, link_target)
    zipOut.close()

create_zip_with_symlink('cpuinfo.zip', 'cpuinfo.txt', '/proc/cpuinfo')

Другие вопросы по теме