use PHP ZipArchive with file as variable - php

In a symfony Controller I have a file (UploadedFile object) as a variable.
I want to open this "variable" with php ZipArchive, and extract it.
But the open() methods is expecting a string, which is the filename in the filesystem. Is there any way to process the file with the ZipArchive and without writing the file variable to the FS?

You can use tmpfile() to create a temporary file, write to it and then use it in the zip. Example:
<?php
$zip = new ZipArchive();
$zip->open(__DIR__ . '/zipfile.zip', ZipArchive::CREATE);
$fp = tmpfile();
fwrite($fp, 'Test');
$filename = stream_get_meta_data($fp)['uri'];
$zip->addFile($filename, 'filename.txt');
$zip->close();
fclose($fp);

Little improve:
$zipContent; // in this variable could be ZIP, DOCX, XLSX etc.
$fp = tmpfile();
fwrite($fp, $zipContent);
$stream = stream_get_meta_data($fp);
$filename = $stream['uri'];
$zip = new ZipArchive();
$zip->open($filename);
// profit!
$zip->close();
fclose($fp);
Just make no sense to create "zipfile.zip" and add file inside, because we already have a variable.

Check out this answer at https://stackoverflow.com/a/53902626/859837
There is a way to create a file which resides in memory only.

Related

cannot add xml file to zip laravel

I want to create a zip folder that includes an xml file and download it. I can download zip but it is empty.
I can create xml and zip :
By the way, $result is the result of ArrayToXml::convert(array,....)
$zip = new ZipArchive;
$fileName = 'example-'.time().'.xml';
$zipName = 'example'.time().'.zip';
if ($zip->open(public_path("storage/zips/".$zipName), ZipArchive::CREATE) === TRUE)
{
Storage::disk('public')->put('/files/'.$fileName, $result);
$zip->addFile(public_path("storage/files/".$fileName), $result);
Storage::disk('public')->put('/zips/'.$zipName, $zip);
$zip->close();
}
return response()->download(storage_path('app\public\zips\\'.$zipName));
How to add xml file to zip. I am new to laravel please help
Have a look at the Madzipper package. Makes working with zip files very easy.
You can create a zipfile as follows:
$fileName = 'example-'.time() . '.xml';
$zip = 'public/zips/example' . time() . '.zip';
Madzipper::make($zip)->addString($fileName, $result)->close();
And then return the path to $zip when downloading the file.
Please have a look at the docs for more info.

PHP: how to retrieve the filename of the file contained in .gz?

My test.gz contains for instance : image1.jpg.
How can I decompress the test.gz so it gives me image1.jpg ?
I tried that method:
$file = gzopen($file_name, 'rb');
$out_file = fopen($out_file_name, 'wb');
but it supposes you need to know the filename contained in the .gz file
regards
You need to use PHP PharData to extract and read its content.
To extract tar or tar.gz :
$phar = new PharData('myphar.tar');
$phar->extractTo('/full/path');
And to list all files, you can use :
archive = new PharData('/upload/directory/myphar.tar.gz');
foreach (new RecursiveIteratorIterator($archive) as $file) {
echo $file . "<br />";
}

PHP ZipArchive download working but not adding files to zip

I know there is many questions regarding the ZipArchive and how it works but after following all these I still can't get my program to work.
I don't know where i'm going wrong because I have die methods in and none of them are getting activated.
$path = "/export/scripts/CLOUD/logs/web/Private_Testbox/Jan_28_2013_16_20_44_atvts78_rollout_config/";
$fileName = explode('/', $path);
$zipname = $fileName[count($fileName) - 2];
$zip = new ZipArchive;
$handle = opendir($path);
//Check whether Zip can be opened
if ($zip->open($zipname, ZIPARCHIVE::CREATE) !== TRUE) {
die("Could not open archive");
}
//Add all files to an array
while ($file = readdir($handle))
{
$zip->addFile($path, $file);
}
closedir($handle);
$zip->close();
//Send zip folder
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename=' . $zipname . '.zip');
readfile($zip);
This file downloads the zip folder but there is never anything in it.
Thanks for help in advance.
You forgot a die() on the ->addFile() call, which means you're assuming that the files actually got added.
readfile() does not return the full path to a file in a directory, only the actual "local" filename, which means you're adding files which don't exist in the script's current working directory. Try:
$zip->addFile($file, $path . $file);
^^^^^^^^^^^^^^^^^^^^^
instead. As it stands now, your code is using $path as the filename to embed in the zip, and directories cannot be used as filenames. And without the $path . $file, addFile is looking in the wrong spot, e.g. it's the equivalent of getcwd() . $file instead.

getimagesize() on stream instead of string

I'm using Valum's file uploader to upload images with AJAX. This script submits the file to my server in a way that I don't fully understand, so it's probably best to explain by showing my server-side code:
$pathToFile = $path . $filename;
//Here I get a file not found error, because the file is not yet at this address
getimagesize($pathToFile);
$input = fopen('php://input', 'r');
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
//Here I get a string expected, resource given error
getimagesize($input);
fclose($input);
$target = fopen($pathToFile, 'w');
fseek($temp, 0, SEEK_SET);
//Here I get a file not found error, because the image is not at the $target yet
getimagesize($pathToFile);
stream_copy_to_stream($temp, $target);
fclose($target);
//Here it works, because the image is at the desired location so I'm able to access it with $pathToFile. However, the (potentially) malicious file is already in my server.
getimagesize($pathToFile);
The problem is that I want to perform some file validation here, using getimagesize(). getimagesize only supports a string, and I only have resources available, which result in the error: getimagesize expects a string, resource given.
It does work when I perform getimagesize($pathTofile) at the end of the script, but then the image is already uploaded and the damage could already have been done. Doing this and performing the check afterwards and then maybe deleting te file seems like bad practice to me.
The only thing thats in $_REQUEST is the filename, which i use for the var $pathToFile. $_FILES is empty.
How can I perform file validation on streams?
EDIT:
the solution is to first place the file in a temporary directory, and perform the validation on the temporary file before copying it to the destination directory.
// Store the file in tmp dir, to validate it before storing it in destination dir
$input = fopen('php://input', 'r');
$tmpPath = tempnam(sys_get_temp_dir(), 'upl'); // upl is 3-letter prefix for upload
$tmpStream = fopen($tmpPath, 'w'); // For writing it to tmp dir
stream_copy_to_stream($input, $tmpStream);
fclose($input);
fclose($tmpStream);
// Store the file in destination dir, after validation
$pathToFile = $path . $filename;
$destination = fopen($pathToFile, 'w');
$tmpStream = fopen($tmpPath, 'r'); // For reading it from tmp dir
stream_copy_to_stream($tmpStream, $destination);
fclose($destination);
fclose($tmpStream);
PHP 5.4 now supports getimagesizefromstring
See the docs:
http://php.net/manual/pt_BR/function.getimagesizefromstring.php
You could try:
$input = fopen('php://input', 'r');
$string = stream_get_contents($input);
fclose($input);
getimagesizefromstring($string);
Instead of using tmpfile() you could make use of tempnam() and sys_get_temp_dir() to create a temporary path.
Then use fopen() to get a handle to it, copy over the stream.
Then you've got a string and a handle for the operations you need to do.
//Copy PHP's input stream data into a temporary file
$inputStream = fopen('php://input', 'r');
$tempDir = sys_get_temp_dir();
$tempExtension = '.upload';
$tempFile = tempnam($tempDir, $tempExtension);
$tempStream = fopen($tempFile, "w");
$realSize = stream_copy_to_stream($inputStream, $tempStream);
fclose($tempStream);
getimagesize($tempFile);

In memory download and extract zip archive

I would like to download a zip archive and unzip it in memory using PHP.
This is what I have today (and it's just too much file-handling for me :) ):
// download the data file from the real page
copy("http://www.curriculummagic.com/AdvancedBalloons.kmz", "./data/zip.kmz");
// unzip it
$zip = new ZipArchive;
$res = $zip->open('./data/zip.kmz');
if ($res === TRUE) {
$zip->extractTo('./data');
$zip->close();
}
// use the unzipped files...
Warning: This cannot be done in memory — ZipArchive cannot work with "memory mapped files".
You can obtain the data of a file inside a zip-file into a variable (memory) with file_get_contentsDocs as it supports the zip:// Stream wrapper Docs:
$zipFile = './data/zip.kmz'; # path of zip-file
$fileInZip = 'test.txt'; # name the file to obtain
# read the file's data:
$path = sprintf('zip://%s#%s', $zipFile, $fileInZip);
$fileData = file_get_contents($path);
You can only access local files with zip:// or via ZipArchive. For that you can first copy the contents to a temporary file and work with it:
$zip = 'http://www.curriculummagic.com/AdvancedBalloons.kmz';
$file = 'doc.kml';
$ext = pathinfo($zip, PATHINFO_EXTENSION);
$temp = tempnam(sys_get_temp_dir(), $ext);
copy($zip, $temp);
$data = file_get_contents("zip://$temp#$file");
unlink($temp);
As easy as:
$zipFile = "test.zip";
$fileInsideZip = "somefile.txt";
$content = file_get_contents("zip://$zipFile#$fileInsideZip");
Old subject but still relevant since I asked myself the same question, without finding an answer.
I ended up writing this function which returns an array containing the name of each file contained in the archive, as well as the decompressed contents of that file:
function GetZipContent(String $body_containing_zip_file) {
$sectors = explode("\x50\x4b\x01\x02", $data);
array_pop($sectors);
$files = explode("\x50\x4b\x03\x04", implode("\x50\x4b\x01\x02", $sectors));
array_shift($files);
$result = array();
foreach($files as $file) {
$header = unpack("vversion/vflag/vmethod/vmodification_time/vmodification_date/Vcrc/Vcompressed_size/Vuncompressed_size/vfilename_length/vextrafield_length", $file);
array_push($result, [
'filename' => substr($file, 26, $header['filename_length']),
'content' => gzinflate(substr($file, 26 + $header['filename_length'], -12))
]);
}
return $result;
}
Hope this is useful ...
You can get a stream to a file inside the zip and extract it into a variable:
$fp = $zip->getStream('test.txt');
if(!$fp) exit("failed\n");
while (!feof($fp)) {
$contents .= fread($fp, 1024);
}
fclose($fp);
If you can use system calls, the simplest way should look like this (bzip2 case). You just use stdout.
$out=shell_exec('bzip2 -dkc '.$zip);

Categories