Como é o arquivo zip e o que podemos fazer a respeito

Bom dia, querido Habr!

Nos últimos meio ano, o caminho tortuoso dos meus projetos de animais de estimação me levou a uma selva, de onde ainda não é possível sair. E tudo começou de forma inofensiva - um site com fotos, mas um senso de perfeccionismo, a busca de um brinde, além de algumas características da minha mentalidade, que transformaram essa caminhada, como planejada originalmente, em uma jornada realmente longa. Bem, tudo bem, como costumava dizer um revolucionário bastante burry: "Aprenda, estude e estude novamente", mas eu, quer ou não, tenho que seguir essa advertência.

Oh, algo que nos distraí do tópico principal. Não vou mais aborrecê-lo com discursos longos, mas vou direto ao assunto.

Crie um arquivo zip


Em princípio, não vou reescrever a especificação aqui. No geral, também não faz sentido descrever a estrutura, porque tudo isso foi feito antes de mim .

Para quem tem preguiça de seguir os links, descreverei brevemente que qualquer arquivo zip deve conter:

  • Entrada do arquivo:
    • Cabeçalho do arquivo local
    • Dados úteis
    • Descritor de dados (opcional, usado quando não sabemos o tamanho do arquivo e seu hash até o lermos até o final)
  • Cabeçalho de arquivo do diretório central (para cada arquivo. É como um sumário do livro, onde cada seção e página na qual ele pode ser encontrado)
  • Fim do diretório central

Sabendo disso, podemos tentar escrever um arquivo simples que conterá apenas dois arquivos:

<?php //        (1.txt  2.txt)   : $entries = [ '1.txt' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc id ante ultrices, fermentum nibh eleifend, ullamcorper nunc. Sed dignissim ut odio et imperdiet. Nunc id felis et ligula viverra blandit a sit amet magna. Vestibulum facilisis venenatis enim sed bibendum. Duis maximus felis in suscipit bibendum. Mauris suscipit turpis eleifend nibh commodo imperdiet. Donec tincidunt porta interdum. Aenean interdum condimentum ligula, vitae ornare lorem auctor in. Suspendisse metus ipsum, porttitor et sapien id, fringilla aliquam nibh. Curabitur sem lacus, ultrices quis felis sed, blandit commodo metus. Duis tincidunt vel mauris at accumsan. Integer et ipsum fermentum leo viverra blandit.', '2.txt' => 'Mauris in purus sit amet ante tempor finibus nec sed justo. Integer ac nibh tempus, mollis sem vel, consequat diam. Pellentesque ut condimentum ex. Praesent finibus volutpat gravida. Vivamus eleifend neque sit amet diam scelerisque lacinia. Nunc imperdiet augue in suscipit lacinia. Curabitur orci diam, iaculis non ligula vitae, porta pellentesque est. Duis dolor erat, placerat a lacus eu, scelerisque egestas massa. Aliquam molestie pulvinar faucibus. Quisque consequat, dolor mattis lacinia pretium, eros eros tempor neque, volutpat consectetur elit elit non diam. In faucibus nulla justo, non dignissim erat maximus consectetur. Sed porttitor turpis nisl, elementum aliquam dui tincidunt nec. Nunc eu enim at nibh molestie porta ut ac erat. Sed tortor sem, mollis eget sodales vel, faucibus in dolor.', ]; //      Lorem.zip,      cwd (      ) $destination = 'Lorem.zip'; $handle = fopen($destination, 'w'); //      ,    ,     ,   "" Central Directory File Header $written = 0; $dictionary = []; foreach ($entries as $filename => $content) { //         Local File Header,     //        ,      . $fileInfo = [ //     'versionToExtract' => 10, //   0,        - 'generalPurposeBitFlag' => 0, //      ,    0 'compressionMethod' => 0, // -    mtime ,    ,      ? 'modificationTime' => 28021, //   , ? 'modificationDate' => 20072, //      .     ,       ,   ? 'crc32' => hexdec(hash('crc32b', $content)), //     .        . //       :) 'compressedSize' => $size = strlen($content), 'uncompressedSize' => $size, //    'filenameLength' => strlen($filename), //  .    ,   0. 'extraFieldLength' => 0, ]; //      . $LFH = pack('LSSSSSLLLSSa*', ...array_values([ 'signature' => 0x04034b50, //  Local File Header ] + $fileInfo + ['filename' => $filename])); //       ,       Central Directory File Header $dictionary[$filename] = [ 'signature' => 0x02014b50, //  Central Directory File Header 'versionMadeBy' => 798, //  .    ,  -  . ] + $fileInfo + [ 'fileCommentLength' => 0, //    . No comments 'diskNumber' => 0, //     0,        'internalFileAttributes' => 0, //    'externalFileAttributes' => 2176057344, //    'localFileHeaderOffset' => $written, //      Local File Header 'filename' => $filename, //  . ]; //      $written += fwrite($handle, $LFH); //    $written += fwrite($handle, $content); } // ,     ,    . //          End of central directory record (EOCD) $EOCD = [ //  EOCD 'signature' => 0x06054b50, //  .    ,   0 'diskNumber' => 0, //      -  0 'startDiskNumber' => 0, //       . 'numberCentralDirectoryRecord' => $records = count($dictionary), //    .    ,     'totalCentralDirectoryRecord' => $records, //   Central Directory Record. //      ,      'sizeOfCentralDirectory' => 0, // ,    Central Directory Records 'centralDirectoryOffset' => $written, //     'commentLength' => 0 ]; //     !   foreach ($dictionary as $entryInfo) { $CDFH = pack('LSSSSSSLLLSSSSSLLa*', ...array_values($entryInfo)); $written += fwrite($handle, $CDFH); } // ,   .  ,    $EOCD['sizeOfCentralDirectory'] = $written - $EOCD['centralDirectoryOffset']; //     End of central directory record $EOCD = pack('LSSSSLLS', ...array_values($EOCD)); $written += fwrite($handle, $EOCD); //  . fclose($handle); echo '  : ' . $written . ' ' . PHP_EOL; echo '     `unzip -tq ' . $destination . '`' . PHP_EOL; echo PHP_EOL; 

Tente executar esse código primitivo e a saída fornecerá um arquivo Lorem.zip que conterá 1.txt e 2.txt.

Porque


Obviamente, qualquer pessoa adequada dirá que escrever arquivadores em php é uma tarefa fútil, especialmente porque para um formato como zip, existem várias implementações prontas para todos os gostos e cores. E no mesmo php existem bibliotecas prontas. Eu vou dizer isso também :)

Mas por que, então, é este artigo inteiro, por que gastei tempo escrevendo e você lendo?
E então, que tudo não é tão simples e saber como o zip funciona, abre algumas possibilidades adicionais para nós.

Em primeiro lugar, espero, pelo menos um pouco, mas ajudará aqueles que querem entender a estrutura do zip.
E, em segundo lugar, criando o arquivo com nossas próprias mãos, temos controle e, mais importante, acesso aos seus dados internos.

Podemos pré-calcular o cabeçalho do arquivo local e o cabeçalho do arquivo do diretório central e, a pedido, gerar um arquivo zip em tempo real com qualquer conteúdo e ordem dos arquivos, simplesmente substituindo esses dados. E sem sobrecarga, exceto E / S.

Ou então, podemos gravar o arquivo morto, carregá-lo, por exemplo, na nuvem, que suporta downloads fragmentados e, conhecendo as compensações de cada arquivo, obter qualquer um deles, como se não estivesse no arquivo, adicionando apenas um título para a solicitação. E então tudo isso pode ser procurado e ...

Ok, não vamos nos antecipar. Se você estiver interessado neste tópico, nos artigos a seguir tentarei considerar essas oportunidades e mostrar como usá-las.


'diskNumber' => 0, // Eu geralmente encontrei 0 em todos os lugares e, em particular, decidi não me aprofundar
Como berez sugeriu, o número do volume no arquivo múltiplo.

Source: https://habr.com/ru/post/pt471066/


All Articles