IO::Compress::Zip читается из файлового дескриптора в буфер

Я пытаюсь создать zip-файл на лету и отправить его в браузер. я использую IO::Compress::Zip с IO::String для этого, но это не работает, как я ожидал от документации.

Сначала мой код:

my $zipped_file;
foreach (@{$par{directory}}) {
    my $input = IO::String->new($_->[0]);
    zip $input => \$zipped_file, Append => 1, BinModeIn => 1|0; # Tried both for BinModeIn
}
return $zipped_file;

$par{directory} является arrayref со следующей структурой:

[
    ['DATA', 'FILENAME'], ['DATA', 'FILENAME'], ...
]

Данные в $par{directory} строка такая:

<?xml version="1.0" encoding="UTF-8"?>
<derivatives>
     <derivative ag="111520420" vhash="d6712f201155a9afb64dc5a5ba369c99">
        //Some data
    </derivative>
</derivatives>

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

Заранее спасибо.

РЕДАКТИРОВАТЬ: Это странно. Я сейчас попробовал это:

use strict;
use warnings;
use IO::Compress::Zip 'zip';

my $xml1 = <<END_XML;
    <?xml version="1.0" encoding="UTF-8"?>
    <derivatives>
        <derivative ag="111520420" vhash="d6712f201155a9afb64dc5a5ba369c99">
            //Some data
        </derivative>
    </derivatives>
END_XML  

my $xml2 = <<END_XML;
    <?xml version="1.0" encoding="UTF-8"?>
    <derivatives2>
         <derivative2 ag="111520420" vhash="d6712f201155a9afb64dc5a5ba369c99">
             //Some data
         </derivative2>
     </derivatives2>
END_XML  

my $files = [[$xml1, 'xml1'], [$xml2, 'xml2']];
my $zipped_file;

foreach (@$files) {
    open my $input, '<', \$_->[0];
    zip $input => \$zipped_file, Append => 1, BinModeIn => 1;
}

open my $fh, '>:raw', 'zip.zip';
print $fh $zipped_file;
close $fh;

Архив до сих пор пуст.

2 ответа

Решение

Опция " Добавить" для zip не делает то, что вы ожидаете. Он не обновляет существующий zip-файл. Он создает новый и добавляет его в файл / буфер, который вы предоставляете. В вашем случае вы получите серию контейнеров на молнии один за другим.

Вот способ добиться того, чего вы хотите, с помощью Archive::Zip::SimpleZip - это всего лишь оболочка для IO::Compress::Zip, написанная для обработки этого типа сценария использования.

use Archive::Zip::SimpleZip ;

my $zipped_file;
my $zip = new Archive::Zip::SimpleZip \$zipped_file ;
foreach (@{$par{directory}}) {
  $zip->addString($_->[0], Name => $_->[1]);
}
$zip->close();
return $zipped_file;

Я думаю, что проблема, скорее всего, связана с тем, что вы делаете с $zipped_file после того, как вы его создали.

Программа ниже работает для меня. Я записал заархивированную строку в двоичный файл, и разархивирование ее с помощью отдельной утилиты доставляет оригинальный XML.

Я бы предпочел отказаться IO::String и использовать встроенный open my $input, '<', \$_->[0] вместо этого, но, кажется, работает в любом случае.

use strict;
use warnings;

use IO::Compress::Zip 'zip';
use IO::String;

my $xml = <<END_XML;
<?xml version="1.0" encoding="UTF-8"?>
<derivatives>
     <derivative ag="111520420" vhash="d6712f201155a9afb64dc5a5ba369c99">
        //Some data
    </derivative>
</derivatives>
END_XML

my $zipped_file;

my $input = IO::String->new($xml);

zip $input => \$zipped_file, Append => 0, BinModeIn => 1;

open my $fh, '>:raw', 'zip.zip';
print $fh $zipped_file;
close $fh;

Обновить

Кстати, кажется, что вообще нет необходимости открывать поток ввода-вывода для входных данных: вы можете просто передать ссылку на скаляр, содержащий данные. Таким образом, выше становится просто

my $zipped_file;

zip \$xml => \$zipped_file, Append => 0, BinModeIn => 1;

open my $fh, '>:raw', 'zip.zip';
print $fh $zipped_file;
close $fh;
Другие вопросы по тегам