Извлечь файл из строки ZIP

У меня есть строка BASE64 zip-файла, содержащего один файл XML.

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

Я бы очень хотел сохранить весь процесс в памяти, поскольку XML имеет только 1-5k.

Было бы неприятно писать zip, извлекать XML, а затем загружать его и удалять все.

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

  1. Первая попытка с php://memory работать не будет, поскольку это поток, который не может быть прочитан такими функциями, как file_get_contents() или ZipArchive::open() . В комментариях есть ссылка на php-bugtracker из-за отсутствия документации по этой проблеме.
  2. Существует поддержка потока ZipArchive с ::getStream() но, как указано в руководстве, она поддерживает только операцию чтения в открытом файле. Таким образом, вы не можете создавать архив на лету с этим.
  3. zip:// также доступна только для чтения. Создайте ZIP-файл с помощью fopen () wrapper
  4. Я также сделал некоторые попытки с другими php wrappers / protocolls, такими как

      file_get_contents("zip://data://text/plain;base64,{$base64_string}#test.txt") $zip->open("php://filter/read=convert.base64-decode/resource={$base64_string}") $zip->open("php://filter/read=/resource=php://memory") 

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


Оригинальный ответ:

Это всего лишь способ временного хранения. Надеюсь, вы справитесь с обработкой zip и анализом xml самостоятельно.

Используйте упаковку php php://memory ( doc ). Имейте в виду, что это полезно только для небольших файлов, потому что оно хранится в памяти – очевидно. В противном случае вместо этого используйте php://temp .

 <?php // the decoded content of your zip file $text = 'base64 _decoded_ zip content'; // this will empty the memory and appen your zip content $written = file_put_contents('php://memory', $text); // bytes written to memory var_dump($written); // new instance of the ZipArchive $zip = new ZipArchive; // success of the archive reading var_dump(true === $zip->open('php://memory')); 

У меня была аналогичная проблема, я закончил это вручную.
https://www.pkware.com/documents/casestudies/APPNOTE.TXT

Это извлекает один файл (только первый), без ошибок / crc, предполагает, что используется дефляция.

 // zip in a string $data = file_get_contents('test.zip'); // magic $head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,0,30)); $filename = substr($data,30,$head['namelen']); $raw = gzinflate(substr($data,30+$head['namelen']+$head['exlen'],$head['csize'])); // first file uncompressed and ready to use file_put_contents($filename,$raw); 

toster-cx правильно, вы должны наградить его очками, это пример, когда zip происходит от ответа на мыло в виде байтового массива (двоичного), содержимое представляет собой XML-файл:

 $objResponse = $objClient->__soapCall("sendBill",array(parameters)); $fileData=unzipByteArray($objResponse->applicationResponse); header("Content-type: text/xml"); echo $fileData; function unzipByteArray($data){ /*this firts is a directory*/ $head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,0,30)); $filename = substr($data,30,$head['namelen']); $if=30+$head['namelen']+$head['exlen']+$head['csize']; /*this second is the actua file*/ $head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,$if,30)); $raw = gzinflate(substr($data,$if+$head['namelen']+$head['exlen']+30,$head['csize'])); /*you can create a loop and continue decompressing more files if the were*/ return $raw; } , $objResponse = $objClient->__soapCall("sendBill",array(parameters)); $fileData=unzipByteArray($objResponse->applicationResponse); header("Content-type: text/xml"); echo $fileData; function unzipByteArray($data){ /*this firts is a directory*/ $head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,0,30)); $filename = substr($data,30,$head['namelen']); $if=30+$head['namelen']+$head['exlen']+$head['csize']; /*this second is the actua file*/ $head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,$if,30)); $raw = gzinflate(substr($data,$if+$head['namelen']+$head['exlen']+30,$head['csize'])); /*you can create a loop and continue decompressing more files if the were*/ return $raw; } 

Если вы знаете имя файла внутри .zip, просто выполните следующее:

 <?php $xml = file_get_contents('zip://./your-zip.zip#your-file.xml'); 

Если у вас простая строка, просто выполните следующее:

 <?php $xml = file_get_contents('compress.zlib://data://text/plain;base64,'.$base64_encoded_string); 

[править] Документация есть: http://www.php.net/manual/en/wrappers.php

Из комментариев: если у вас нет кодированной в base64 строки, вам необходимо выполнить urlencode () перед использованием data:// wrapper.

 <?php $xml = file_get_contents('compress.zlib://data://text/plain,'.urlencode($text)); 

[edit 2] Даже если вы уже нашли решение с файлом, есть решение (для проверки), которое я не видел в вашем ответе:

 <?php $zip = new ZipArchive; $zip->open('data::text/plain,'.urlencode($base64_decoded_string)); $zip2 = new ZipArchive; $zip2->open('data::text/plain;base64,'.urlencode($base64_string)); 

если вы хотите прочитать содержимое файла из zip like и xml внутри, вы смотрите на это, я использую его для подсчета слов из docx (это zip)

 if (!function_exists('docx_word_count')) { function docx_word_count($filename) { $zip = new ZipArchive(); if ($zip->open($filename) === true) { if (($index = $zip->locateName('docProps/app.xml')) !== false) { $data = $zip->getFromIndex($index); $zip->close(); $xml = new SimpleXMLElement($data); return $xml->Words; } $zip->close(); } return 0; } }