2013-02-28 8 views
20

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

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

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

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

ответ

15

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

  1. Первая попытка с php://memory не будет работать, потому что она находится поток, который не может быть прочитан как функции file_get_contents() или ZipArchive::open(). В комментариях есть ссылка на php-bugtracker из-за отсутствия документации по этой проблеме.
  2. Существует поддержка потока ZipArchive с ::getStream(), но, как указано в руководстве, она поддерживает только операцию чтения в открытом файле. Таким образом, вы не можете создавать архив на лету с этим.
  3. zip:// обертка также доступен только для чтения: Create ZIP file with fopen() wrapper
  4. Я также сделал несколько попыток с другими PHP оберток/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')); 
+1

У меня, похоже, проблема. Запуск zip не удается, даже если он, похоже, написал zip-файл в память. Я пробовал file_get_contents из php: // памяти, и это строка (0) ... – transilvlad

+2

Да, извините - мой код в этом случае не так. Методы file_put_contents() не работают в потоках ввода-вывода: https://bugs.php.net/bug.php?id=50886&edit=2. Поэтому вам нужно работать с 'fopen()', 'fwrite()' и 'stream_get_contents()' для записи/чтения в 'php: // memory' – HenningCash

+1

Очевидно, вы уже выяснили: это невозможно. Я просто добавил несколько причин для ответа. Удачи в любом случае! – HenningCash

-1

, если вы хотите, чтобы прочитать содержимое файла из почтового индекса, как и XML внутри вы Шоуда смотреть на это я использовать его для подсчета слов из DOCX (которым является застежка-молния)

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; 
    } 
} 
+0

У меня есть строка BASE64 архива Zip. Я не хочу писать на диск для распаковки. – transilvlad

1

Если вы знаете, имя файла внутри .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://.

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

[править 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)); 
+2

Я только что проверил ваш второй пример. '$ xml', кажется, двоичные данные (zip-файл, в основном), а не извлеченный XML-файл. – pixelistik

+1

Я тестировал, тот же результат. – transilvlad

+0

Hum. Возможно, вам следует комбинировать zip: // и data: // вместо compress.zlib. – Savageman

10

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

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

// 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); 
2

Toster-сх был прав, вы должны присудить ему очки, это пример, когда молния исходит от ответа мыльного как массив байт (бинарного), содержание представляет собой файл 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; 
} 
Смежные вопросы