[英]Extract a file from a ZIP string
我有一个包含一个 XML 文件的 zip 文件的 BASE64 字符串。
关于如何在不必处理磁盘上的文件的情况下获取 XML 文件的内容的任何想法?
我非常希望将整个过程保存在内存中,因为 XML 只有 1-5k。
必须编写 zip,提取 XML,然后加载它并删除所有内容会很烦人。
我遇到了类似的问题,我最终手动完成了。
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);
经过几个小时的研究,我认为在没有临时文件的情况下处理 zip 令人惊讶的是不可能的:
php://memory
将不起作用,因为它是一个无法被file_get_contents()
或ZipArchive::open()
类的函数读取的流。 由于缺少此问题的文档,评论中提供了指向 php-bugtracker 的链接。::getStream()
的流支持ZipArchive
但如手册中所述,它仅支持对打开文件的读取操作。 所以你不能用它即时建立档案。zip://
包装器也是只读的: 使用 fopen() 包装器创建 ZIP 文件我还对其他 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")
但对我来说,它们根本不起作用,即使手册中有类似的例子。 所以你必须吞下药丸并创建一个临时文件。
原答案:
这只是临时存储的方式。 我希望您自己管理 xml 的 zip 处理和解析。
使用 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'));
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;
}
如果您知道 .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 编码的字符串,则需要在使用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));
如果您在 Linux 上运行并管理系统。 您可以使用 tmpfs 挂载一个小型 ramdisk,然后标准的 file_get / put 和 ZipArchive 函数将起作用,除了它不写入磁盘,而是写入内存。 要使其永久就绪,fstab 类似于:
/media/ramdisk tmpfs nodev,nosuid,noexec,nodiratime,size=2M 0 0
相应地设置您的尺寸和位置,使其适合您。 使用 php 挂载 ramdisk 并在使用后将其删除(如果它甚至具有特权)可能比仅写入磁盘效率低,除非您一次性处理大量文件。 虽然这不是纯 php 解决方案,也不是可移植的。 您仍然需要在使用后删除“文件”,或者让操作系统清理旧文件。 它们不会在重新启动或重新安装 ramdisk 后持续存在。
这个想法来自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 ) );
如果你想从 zip 和 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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.