php 从 ZIP 字符串中提取文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15140063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Extract a file from a ZIP string
提问by transilvlad
I have a BASE64 string of a zip file that contains one single XML file.
我有一个包含一个 XML 文件的 zip 文件的 BASE64 字符串。
Any ideas on how I could get the contents of the XML file without having to deal with files on the disk?
关于如何在不必处理磁盘上的文件的情况下获取 XML 文件的内容的任何想法?
I would like very much to keep the whole process in the memory as the XML only has 1-5k.
我非常希望将整个过程保留在内存中,因为 XML 只有 1-5k。
It would be annoying to have to write the zip, extract the XML and then load it up and delete everything.
必须编写 zip,提取 XML,然后加载它并删除所有内容会很烦人。
采纳答案by HenningCash
After some hours of research I think it's surprisingly not possible do handle a zip without a temporary file:
经过几个小时的研究,我认为在没有临时文件的情况下处理 zip 令人惊讶的是不可能的:
- The first try with
php://memorywill not work, beacuse it's a stream that cannot be read by functions likefile_get_contents()orZipArchive::open(). In the comments is a link to the php-bugtracker for the lack of documentation of this problem. - There is a stream support
ZipArchivewith::getStream()but as stated in the manual, it only supports reading operation on an opened file. So you cannot build a archive on-the-fly with that. - The
zip://wrapper is also read-only: Create ZIP file with fopen() wrapper I also did some attempts with the other php wrappers/protocolls like
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")but for me they don't work at all, even if there are examples like that in the manual. So you have to swallow the pill and create a temporary file.
- 第一次尝试 with 是
php://memory行不通的,因为它是一个无法被像file_get_contents()or 之类的函数读取的流ZipArchive::open()。由于缺少此问题的文档,评论中提供了指向 php-bugtracker 的链接。 - 有一个流支持
ZipArchive,::getStream()但正如手册中所述,它只支持对打开文件的读取操作。所以你不能用它即时建立档案。 - 该
zip://包装也只读:创建ZIP文件,fopen()函数包装 我还对其他 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")但对我来说,它们根本不起作用,即使手册中有类似的例子。所以你必须吞下药丸并创建一个临时文件。
Original Answer:
原答案:
This is just the way of temporary storing. I hope you manage the zip handling and parsing of xml on your own.
这只是临时存储的方式。我希望您自己管理 xml 的 zip 处理和解析。
Use the php php://memory(doc) wrapper. Be aware, that this is only usefull for small files, because its stored in the memory - obviously. Otherwise use php://tempinstead.
使用 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'));
回答by toster-cx
I had a similar problem, I ended up doing it manually.
https://www.pkware.com/documents/casestudies/APPNOTE.TXT
我遇到了类似的问题,我最终手动完成了。
https://www.pkware.com/documents/casestudies/APPNOTE.TXT
This extracts a single file (just the first one), no error/crc checks, assumes deflate was used.
这将提取单个文件(只是第一个文件),没有错误/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);
回答by Boris
toster-cx had it right,you should award him the points, this is an example where the zip comes from a soap response as a byte array (binary), the content is an XML file:
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;
}
回答by keV
If you are running on Linux and have administration of the system. You could mount a small ramdisk using tmpfs, the standard file_get / put and ZipArchive functions will then work, except it does not write to disk, it writes to memory. To have it permanently ready, the fstab is something like:
如果您在 Linux 上运行并管理系统。您可以使用 tmpfs 挂载一个小型 ramdisk,然后标准的 file_get / put 和 ZipArchive 函数将起作用,除了它不写入磁盘,而是写入内存。要使其永久就绪,fstab 类似于:
/media/ramdisk tmpfs nodev,nosuid,noexec,nodiratime,size=2M 0 0
Set your size and location accordingly so it suits you. Using php to mount a ramdisk and remove it after using it (if it even has the privileges) is probably less efficient than just writing to disk, unless you have a massive number of files to process in one go. Although this is not a pure php solution, nor is it portable. You will still need to remove the "files" after use, or have the OS clean up old files. They will of coarse not persist over reboots or remounts of the ramdisk.
相应地设置您的尺寸和位置,使其适合您。使用 php 挂载 ramdisk 并在使用后将其删除(如果它甚至具有特权)可能比仅写入磁盘效率低,除非您一次性处理大量文件。虽然这不是纯 php 解决方案,也不是可移植的。您仍然需要在使用后删除“文件”,或者让操作系统清理旧文件。它们不会在重新启动或重新安装 ramdisk 后持续存在。
回答by Savageman
If you know the file name inside the .zip, just do this:
如果您知道 .zip 中的文件名,请执行以下操作:
<?php
$xml = file_get_contents('zip://./your-zip.zip#your-file.xml');
If you have a plain string, just do this:
如果您有一个普通字符串,请执行以下操作:
<?php
$xml = file_get_contents('compress.zlib://data://text/plain;base64,'.$base64_encoded_string);
[edit] Documentation is there: http://www.php.net/manual/en/wrappers.php
[编辑] 文档在那里:http: //www.php.net/manual/en/wrappers.php
From the comments: if you don't have a base64 encoded string, you need to urlencode() it before using the data://wrapper.
来自评论:如果您没有 base64 编码的字符串,则需要在使用data://包装器之前 urlencode() 它。
<?php
$xml = file_get_contents('compress.zlib://data://text/plain,'.urlencode($text));
[edit 2] Even if you already found a solution with a file, there's a solution (to test) I didn't see in your answer:
[编辑 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));
回答by ddjikic
if you want to read the content of a file from zip like and xml inside you shoud look at this i use it to count words from docx (wich is a zip )
如果你想从 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;
}
}

