从ZIP字符串中提取文件

时间:2013-02-28 16:02:22

标签: php

我有一个包含一个XML文件的zip文件的BASE64字符串。

关于如何在不必处理磁盘上的文件的情况下获取XML文件内容的任何想法?

我非常希望将整个过程保留在内存中,因为XML只有1-5k。

必须编写zip,提取XML然后加载它并删除所有内容会很烦人。

7 个答案:

答案 0 :(得分:16)

经过几个小时的研究后,我认为在没有临时文件的情况下处理拉链是不可能的:

  1. 使用php://memory的第一次尝试无效,因为它是file_get_contents()ZipArchive::open()等功能无法读取的流。在评论中是一个链接到php-bugtracker缺乏这个问题的文档。
  2. ZipArchive的流支持::getStream()但是如手册中所述,它仅支持对打开的文件进行读取操作。因此,您无法即时构建存档。
  3. zip://包装器也是只读的:Create ZIP file with fopen() wrapper
  4. 我也尝试了其他php包装器/协议,如

     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")
    

    但对我来说,他们根本不工作,即使手册中有这样的例子。所以你必须吞下药丸并创建一个临时文件。


  5. 原始答案:

    这只是临时存储的方式。我希望你自己管理xml的zip处理和解析。

    使用php php://memorydoc)包装器。请注意,这仅对小文件有用,因为它存储在内存中 - 显然。否则请改用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 :(得分:12)

我有类似的问题,我最终手动完成了 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 :(得分:3)

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

答案 3 :(得分: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);

[edit]文档在那里:http://www.php.net/manual/en/wrappers.php

来自评论:如果你没有base64编码的字符串,你需要在使用data://包装器之前使用urlencode()它。

<?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));

答案 4 :(得分:1)

如果您在Linux上运行并具有系统管理权限。您可以使用tmpfs挂载一个小型ramdisk,然后标准的file_get / put和ZipArchive函数将起作用,除非它不写入磁盘,而是写入内存。 要使其永久就绪,fstab类似于:

/media/ramdisk tmpfs nodev,nosuid,noexec,nodiratime,size=2M 0 0

相应地设置大小和位置,以适合您。 使用php挂载一个ramdisk并在使用后将其删除(如果它甚至具有特权)可能会比仅写入磁盘效率低,除非您一次要处理大量文件。 尽管这不是纯PHP解决方案,但也不是可移植的。 使用后,您仍然需要删除“文件”,或者让操作系统清理旧文件。 它们将不会在重新启动或重新安装ramdisk时持久存在。

答案 5 :(得分:0)

这个想法来自于 toster-cx 对于处理格式错误的 zip 文件也非常有用!

我有一个标题中缺少数据,所以我不得不使用他的方法提取中央目录文件标题

$CDFHoffset = strpos( $zipFile, "\x50\x4b\x01\x02" );                                                       
$CDFH = unpack( "Vsig/vverby/vverex/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr( $zipFile, $CDFHoffset, 46 ) );

答案 6 :(得分:-1)

如果你想从zip和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;
    }
}