I need a little help here.
I intended to create 2 csv files in PHP and have the browser download them.
This will happen when users click a button. However, I discovered that only 1 file download is permitted for each http request. I stumbled upon this stackoverflow post PHP Create Multiple CSV Files in Memory then Compress. The solution creates 3 csv files in-memory and added them into a zip file and download it to the client's browser.
$headers = array('id', 'name', 'age', 'species');
$records = array(
array('1', 'gise', '4', 'cat'),
array('2', 'hek2mgl', '36', 'human')
);
// create your zip file
$zipname = 'file.zip';
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
// loop to create 3 csv files
for ($i = 0; $i < 3; $i++) {
// create a temporary file
$fd = fopen('php://temp/maxmemory:1048576', 'w');
if (false === $fd) {
die('Failed to create temporary file');
}
// write the data to csv
fputcsv($fd, $headers);
foreach($records as $record) {
fputcsv($fd, $record);
}
// return to the start of the stream
rewind($fd);
// add the in-memory file to the archive, giving a name
$zip->addFromString('file-'.$i.'.csv', stream_get_contents($fd) );
//close the file
fclose($fd);
}
// close the archive
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zipname);
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
// remove the zip archive
unlink($zipname);
I tried the solution marked as correct, but it didn't work.
A zip file was downloaded, but it is empty.
Any help would be appreciated.
PS : Sorry, bad english, I'm not a native speaker.
Your code is correct. But i also had problems creating the zip-file because of permissions. You need to set right permission of the folder your script is running in and also of the script itself. Assuming you are under linux use chmod recursivly (-R):
chmod -R 666 your_folder
Related
I am trying this piece of code found on the internet, which helps to create a ZipArchive with files on PHP.
I am trying this on my server, which is an AWS EC2 with Linux Ubuntu where a web server is running. I run the following code :
<?php
function createZipAndDownload($files, $filesPath, $zipFileName)
{
// Create instance of ZipArchive. and open the zip folder.
$zip = new ZipArchive();
$r = $zip->open($zipFileName, ZipArchive::CREATE);
var_dump($r); echo "<br>";
// Adding every attachments files into the ZIP.
foreach ($files as $file) {
$r = $zip->addFile($filesPath . $file, $file);
var_dump($r); echo "<br>";
}
$r = $zip->close();
var_dump($r); echo "<br>";
// Download the created zip file
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename = $zipFileName");
header("Content-length: " . filesize($zipFileName));
header("Pragma: no-cache");
header("Expires: 0");
readfile("$zipFileName");
exit;
}
// Files which need to be added into zip
$files = array('twiglet-1120x720.jpg','22vwqq.jpg','fb4eb7f8aa5431b0b4e26365ebd59933-239x300.jpg');
// Directory of files
$filesPath = 'https://peakon.com/wp-content/uploads/2018/08/';
// Name of creating zip file
$zipName = 'document.zip';
echo createZipAndDownload($files, $filesPath, $zipName);
?>
Then, I have this output :
bool(true)
bool(false)
bool(false)
bool(false)
bool(true)
I understand that the ZipArchive is created, but files are not sent inside the ZipArchive. Nevertheless, the file is downloaded once finished and when I want to open it, it's written that "the file is damaged" and I can't open it.
Can you help me ? Do you know why it is not working ? I could like to insert theses images inside and download it ? (images are just examples)
Thank you in advance for your help.
addFile takes a file system path, not an HTTP URL. There's several ways to fix it but for your case, it might just be easiest to use file_get_contents along with addFromString. file_get_contents does accept URLs, weirdly enough. Note that the parameters for addFromString aren't in the same order as addFile.
$r = $zip->addFromString($file, file_get_contents($filesPath . $file));
In the long run, however, I'd recommend downloading the files, checking the results and throwing error messages as needed, but this should get you started at least.
edit
The parameter ZipArchive::CREATE will only create an archive if it doesn't exist already, so it might actually try opening an existing file and, with your previous attempts, this might result in you opening a corrupt file. Instead, I would recommend using either ZipArchive::OVERWRITE to start over, or ZipArchive::EXCL to guarantee that an existing file is not touched.
Putting this all together, this code, exactly as-is, works when I test it:
function createZipAndDownload($files, $filesPath, $zipFileName)
{
// Create instance of ZipArchive. and open the zip folder.
$zip = new ZipArchive();
$r = $zip->open($zipFileName, ZipArchive::OVERWRITE);
var_dump($r);
echo "<br>";
// Adding every attachments files into the ZIP.
foreach ($files as $file) {
$r = $zip->addFromString($file, file_get_contents($filesPath . $file));
var_dump($r);
echo "<br>";
}
$r = $zip->close();
var_dump($r);
echo "<br>";
// Download the created zip file
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename = $zipFileName");
header("Content-length: " . filesize($zipFileName));
header("Pragma: no-cache");
header("Expires: 0");
readfile($zipFileName);
exit;
}
// Files which need to be added into zip
$files = array('twiglet-1120x720.jpg', '22vwqq.jpg', 'fb4eb7f8aa5431b0b4e26365ebd59933-239x300.jpg');
// Directory of files
$filesPath = 'https://peakon.com/wp-content/uploads/2018/08/';
// Name of creating zip file
$zipName = 'document.zip';
createZipAndDownload($files, $filesPath, $zipName);
Hi i am making a download feature on my website and it is running however all the files are corrupted due to a recurring error readfile(): Filename cannot be empty in 'my/file/location' I don't understand why this is happening because according to every post I have looked at I am doing the download->zipfiles procedure correctly. Please help.
By the way i think the key to the answer may be in the fact that when i open the zip in a text file it reads:
Warning: file_get_contents(/uploads/): failed to open stream: No such file or directory in /home/ghostx19/public_html/phDownloader.php on line 21
the error is repeated 10 times because of the loop obviously
My Code:
<?php
//define stuff
$dir = "./uploads";
$allfiles = glob($dir.'*.{aiff}', GLOB_BRACE);
// create zip
$zip = new ZipArchive();
$zip_name = "zipfile.zip";
if($zip->open($zip_name, ZIPARCHIVE::CREATE)!==TRUE){
$error .= "* Sorry ZIP creation failed at this time";
}
//array of random files
$n=1;
while ($n<=10){
$n ++;
$randomfile = $allfiles[array_rand($allfiles)];
$path = "./uploads".$randomfile;
$zip->addFile(basename($path), file_get_contents($path));
}
// //present for download
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zip_name);
readfile('zipfile.zip');
if(file_exists('zipfile.zip'))
{
unlink('zipfile.zip');
}
?>
I am trying to randomly download some files.
I apologize because I posted it earlier but can someone explain what I am doing wrong in detail?
I can not seem to debug it as I know minimal php.
<?php
$rootPath = realpath('./uploads');
//make zip file
$zip = new ZipArchive();
$zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$nfiles = glob($rootPath.'*.{aiff}', GLOB_BRACE);
$files = array_rand($nfiles, 20);
foreach($files as $file) {
$pathtofile = $nfiles[$file];
if (!$file->isDir())
{
// Get path for current file
$filePath = $pathtofile->getRealPath();
$relativePath = str_replace($rootPath, "", $file);
// Add file to zip
$zip->addFile($filePath, $relativePath);
}
}
//-------------------------------------------//
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment;
filename='.'generatedsounds.zip');
header('Content-Length: ' . filesize('file.zip'));
readfile('file.zip');
if(file_exists('file.zip'))
{
unlink('file.zip');
}
?>
You're calling ->isDir() method which is not defined, And you're calling ->getRealPath() which is also not defined.
And you're getting all files then selecting 20 random files from the array (why??)
While you are rewriting the headers, errors won't be shown in browser, try without rewriting headers which means it's kept as html header and test until you get a binary file output then add the headers back so you have a working code.
I have a requirement to create 3 CSV files (in memory) during a single HTTP request, ZIP the files into a single compressed file and return the compressed file as a HTTP response.
I have the following code to create the zip file...
$files = array($file1, $file2);
$zipname = 'file.zip';
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
foreach ($files as $file) {
$zip->addFile($file);
}
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zipname);
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
However, I don't know how to create the CSV files in memory.
How can I achieve this?
Try this...
// some data to be used in the csv files
$headers = array('id', 'name', 'age', 'species');
$records = array(
array('1', 'gise', '4', 'cat'),
array('2', 'hek2mgl', '36', 'human')
);
// create your zip file
$zipname = 'file.zip';
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
// loop to create 3 csv files
for ($i = 0; $i < 3; $i++) {
// create a temporary file
$fd = fopen('php://temp/maxmemory:1048576', 'w');
if (false === $fd) {
die('Failed to create temporary file');
}
// write the data to csv
fputcsv($fd, $headers);
foreach($records as $record) {
fputcsv($fd, $record);
}
// return to the start of the stream
rewind($fd);
// add the in-memory file to the archive, giving a name
$zip->addFromString('file-'.$i.'.csv', stream_get_contents($fd) );
//close the file
fclose($fd);
}
// close the archive
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zipname);
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
// remove the zip archive
// you could also use the temp file method above for this.
unlink($zipname);
I've just tested this on my machine and it works fine.
I used this link as a reference, It may be useful.
MetaShock Reference
You can use php's memory wrapper:
$zipname = 'php://memory';
On systems having /dev/shm filesystem you can create files there, they will be kept only in memory and only accessible to current process. Don't forget to remove them after sending, web server process will keep running.
This is a code i found over internet, intended to create a .zip of the entire folder and download it.
I want to do a few changes, but everything I tried doesn't work. What I want:
Make it generic (i.e. I want to have that makezip.php in root folder. I've a file manager, and everytime I call this script from some location (ex. www.domain.com/files/media/images/ it gets that folder).
Delete the zip after the download (I think I did it well, using the comand unlinkbut I'm not sure that's the right way.
Remove that . and .. folder that the zip gets too.
Export the zip with the name date.hour.directory.zip (ex 18Sep2013_13-26-02_images.zip). I tried to do this
$fd = getcwd();
$filename = date("dMY_H:i:s").'_'.$fd.'.zip';
but it didn't worked, it only gets the $fd variable.
I know it isn't supposed to give you all the code and expect that you will look on it nor debug it, but I've tried everything and nothing of it works. I'm learning by myself and I'm a bit newbie on php.
Best regards
<?php
function download($file) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
unlink($file);
exit;
}
$directory = '.';
$files = scandir($directory);
if(empty($files))
{
echo("You haven't selected any file to download.");
}
else
{
$zip = new ZipArchive();
$filename = date("dMY_H:i:s").'_arquivo.zip'; //adds timestamp to zip archive so every file has unique filename
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) { // creates new zip archive
exit("Cannot open <$filename>\n");
}
$N = count($files);
for($i=0; $i < $N; $i++)
{
$zip->addFile($files[$i], $files[$i]); //add files to archive
}
$numFiles = $zip->numFiles;
$zip->close();
$time = 8; //how long in seconds do we wait for files to be archived.
$found = false;
for($i=0; $i<$time; $i++){
if($numFiles == $N){ // check if number of files in zip archive equals number of checked files
download($filename);
$found = true;
break;
}
sleep(1); // if not found wait one second before continue looping
}
if($found) { }
else echo "Sorry, this is taking too long";
}
?>
Ok, I've figured out my problems by myself, and I'm sharing the solution for those who want.
Make it generic (i.e. I want to have that makezip.php in root folder. I've a file manager, and everytime I call this script from some location (ex. www.domain.com/files/media/images/ it gets that folder).
I modified the code on index.php, and made a few changes on zip.php (the file which is handling the zipping process. Simples solution.. Dumb!
1 - index.php - just put a simple link
Download folder as a .zip
2 - zip.php
$directory = $_REQUEST['dirz'];
Delete the zip after the download (I think I did it well, using the comand unlinkbut I'm not sure that's the right way.
Yes, it's correct. (on zip.php)
unlink($ZIPNAME);
Remove that . and .. folder that the zip gets too.
$direct = array_diff(scandir($directory), array(".","..","error_log"));
foreach($direct as $file){
if(is_file($directory.'/'.$file)){
$zip->addFile($directory.'/'.$file, $file);
}
}
Export the zip with the name date.hour.directory.zip (ex 18Sep2013_13-26-02_images.zip). I tried to do this $fd = getcwd(); $filename = date("dMY_H:i:s").'_'.$fd.'.zip'; but it didn't worked, it only gets the $fd variable.
Simple as:
$zipname = date("dMY_H-i-s").'_'.basename($directory).'.zip';
Best regards