I'm writing a script that needs to archive folders and files inside, but I can not figure out how to do it if there's another folder inside one folder. I will explain by example, so the norms work
Warning: ZipArchive::close(): Read error: Is a directory in /путь до скрипта/public_html/crm/drive/drive.php on line 102
Folder +
File1
File2
And so on (so it works)
But does not want to work like that
Folder +
File1
FOLDER (this does not work)
The question is how to make it so that if the script saw the folder it also downloaded and if I saw the folder inside that folder also downloaded files in the folders, respectively? Here is my script
if (isset($_POST['createPath'])) {//Check that the button is clicked
$zip = new ZipArchive(); // Create an archive
$zip_name = time().".zip"; // file name
if ($zip->open($zip_name, ZipArchive::CREATE) !== true) { // open file
die ("Could not open archive");//If the file does not open
}
$var = $_POST["pathUpload"];// Array of variables that are passed through the form
foreach ($var as $key_var) {// We process the array in a loop
$iterator = new RecursiveDirectoryIterator($key_var);//There is a recursive search of the file system directories
foreach ($iterator as $key => $value) {// We process an array of files
$path = pathinfo($value);//Check the path or revert the path to the file
if ($path['basename'] == '.' || $path['basename'] == '..') continue;//Check those files that you download if there are points in the files then download
$zip->addFile(realpath($key), $key);//Add the file to the server
}
$zip->close();//Close archive
if (file_exists($zip_name)) {
// Give the file to download
header('Content-type: application/zip', 'charset=utf-8');
header('Content-Disposition: attachment; filename="'.$zip_name.'"');
ob_end_flush();//Buffering since without it nothing will work
readfile($zip_name); //Read the file
unlink($zip_name);//Delete the variable
}
}
}
The error because you are trying to add directory to Zip with method
// this function only for adding files!
public function addFile ($filename, $localname = null, $start = 0, $length = 0) {}
the method allows you to add directory
public function addEmptyDir ($dirname) {}
The other issue you have, is you are using directory iterator in wrong way.
// this way only loop on directories in
$iterator = new RecursiveDirectoryIterator($key_var);//There is a recursive search of the file system directories
the right way is to use RecursiveIteratorIterator on RecursiveDirectoryIterator - have a look the options in the docs.
example:
// the right way to recursive get list of the file system directories and files
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($key_var, RecursiveDirectoryIterator::SKIP_DOTS), // skip . and ..
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore "Permission denied"
);
so to get it to work, your code should look like this:
<?php
if (isset($_POST['createPath'])) {//Check that the button is clicked
$zip = new ZipArchive(); // Create an archive
$zip_name = time() . ".zip"; // file name
if ($zip->open($zip_name, ZipArchive::CREATE) !== true) { // open file
die ("Could not open archive");//If the file does not open
}
$var = $_POST["pathUpload"];// Array of variables that are passed through the form
foreach ($var as $key_var) {// We process the array in a loop
// There is a recursive search of the file system directories
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($key_var, RecursiveDirectoryIterator::SKIP_DOTS), // skip . and ..
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore "Permission denied"
);
// all directories and subdir
foreach ($iterator as $path => $dir) {
if (!$dir->isDir()) && is_file($path) {
$zip->addFile(realpath($path)); //Add the file to the server
}
if ($dir->isDir()) {
// do nothing
}
}
$zip->close(); //Close archive
if (file_exists($zip_name)) {
// Give the file to download
header('Content-type: application/zip', 'charset=utf-8');
header('Content-Disposition: attachment; filename="' . $zip_name . '"');
ob_end_flush();//Buffering since without it nothing will work
readfile($zip_name); //Read the file
unlink($zip_name);//Delete the variable
}
}
}
happy coding :)
Related
I am trying to access a file in an SFTP folder server using phpseclib. But when I try using $sftp->get, it returns false. I am not sure how to debug the problem at all.
public function get_file_from_ftps_server()
{
$sftp = new \phpseclib\Net\SFTP(getenv('INSTRUM_SERVER'));
if (!$sftp->login(getenv('INSTRUM_USERNAME'), getenv('INSTRUM_PASSWORD'))) {
exit('Login Failed');
}
$this->load->helper('file');
$root = dirname(dirname(__FILE__));
$root .= '/third_party/collections_get/';
$path_to_server = 'testdownload/';
$result = $sftp->get($path_to_server, $root);
var_dump($result);
}
In the $result, I get a false and I am not sure why its happening, I read their documentation but still not sure. Root is the directory where I want my information to be stored. Right now I only added a trial.xml file there, but also wondering how can I get multiple files if its in the folder.
Here is a picture of the server structure:
Normally when I use sftp, I normally change directory and then try to download the information.
$sftp->pwd(); // This will show you are in the root after connection.
$sftp->chdir('./testdownload'); // this will go inside the test directory.
$get_path = $sftp->pwd()
//If you want to download multiple data, use
$x = $sftp->nlist();
//Loop through `x` and then download the file using.
$result = $sftp->get($get_path); // Normally I use the string information that is returned and then download using
file_put_contents($root, $result);
// Root is your directory, and result is the string.
The Net_SFTP.get method can download a single file only. You cannot use it to download a whole directory.
If you want to download whole directory, you have to use one of the "list" methods (Net_SFTP.nlist or Net_SFTP.rawlist) to retrieve list of files and then download the files one-by-one.
<?php
use phpseclib\Net\SFTP;
$sftp = new SFTP("server");
if(!$sftp->login("username", "password")) {
throw new Exception("Connection failed");
}
// The directory you want to download the contents of
$sftp->chdir("/remote/system/path/");
// Loop through each file and download
foreach($sftp->nlist() as $file) {
if($file != "." && $file != "..")
$sftp->get("/remote/system/path/$file", "/local/system/path/$file");
}
?>
I'm a bit late but nonetheless I wanted to share this.
The approach I take is to use zip files to download folders. The reason for this is that you will have a feedback that something is being downloaded.
If you don't want that, simply remove the things related to zip. Then, remove the headers and replace them with $sftp->get("remote/file", "local/file");
<?PHP
use phpseclib\Net\SFTP;
$sftp = new SFTP("IP:Port");
if(!$sftp->login("username", "password")) throw new Exception("Connection failed");
# Create directory variable
$directory = "/remote/path/";
# Set directory
$sftp->chdir($directory);
# File Name
$name = 'file';
# Retrieve file
$file = $sftp->get('file');
# Check if is folder
if ($sftp->is_dir($file)) {
# Temporarily file
$tmp = sys_get_temp_dir()."\\yourSite_".rand().".zip";
# Create new Zip Archive.
$zip = new ZipArchive();
# Open new Zip file
$zip->open($tmp, ZipArchive::CREATE | ZipArchive::OVERWRITE);
function recursive($src, $zip, $sftp) {
# Loop Through files
foreach ($sftp->nlist($src) as $file) {
# Skip . & ..
if ($file == "." || $file == "..") continue;
if (!$sftp->is_file($src . "/" . $file)) {
# Make directory
$zip->addEmptyDir($src . "/" . $file);
# Run the loop again
recursive($src . "/" . $file, $zip, $sftp);
} else {
# Add file to zip within folder
$zip->addFromString($src . "/" . $file, $sftp->get($src . "/" . $file));
}
}
}
# Run Recursive loop
recursive($name, $zip, $sftp);
# Close zip file
$zip->close();
header('Content-Description', 'File Transfer');
header('Content-type', 'application/zip');
header('Content-Disposition', 'attachment; filename="' . $name . '.zip"');
header('Content-length', filesize($tmp));
echo file_get_contents($tmp);
# Delete temporarily file
unlink($tmp);
return;
}
# Otherwise download single file
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=\"". $name ."\"");
echo $file;
return;
I am currently working on a tool made with PHP (quite newbie with this technology...) which should generate zip files with a set of files inside. This set of files can be:
Basic files (mutliple formats)
Full directories (will be added into the resulting zip as a new zipped file - ZIP inside the final ZIP)
The thing is that when the zip files contains simple files it is downloaded properly but when the file contains the "Full directory zip file" then the resulting ZIP file get corrupted...
Below the code I am currently using (sorry if its a bit messy but is the first time I work with PHP...)
function ZipFiles($fileArr,$id) {
$destination = "{$_SERVER['DOCUMENT_ROOT']}/wmdmngtools/tempFiles/WMDConfigFiles_".$id.".zip";
$valid_files = array();
//if files were passed in...
if(is_array($fileArr)) {
//cycle through each file
foreach($fileArr as $file) {
if(is_dir($file)) {
//If path is a folder we zip it and put it on $valid_files[]
$resultingZipPath = "{$_SERVER['DOCUMENT_ROOT']}/wmdmngtools/tempFiles/".basename($file)."_FOLDER.zip";
ZipFolder($file,$resultingZipPath );
$valid_files[] = $resultingZipPath ;
}
else {
//If path is not a folder then we make sure the file exists
if(file_exists("{$_SERVER['DOCUMENT_ROOT']}/wmdmngtools/tempFiles/".$file)) {
$valid_files[] = $file;
}
}
}
}
//if we have good files...
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile("{$_SERVER['DOCUMENT_ROOT']}/wmdmngtools/tempFiles/".$file,$file);
}
$zip->close();
return $destination;
}
else
{
return "";
}
}
function ZipFolder($source, $destination) {
// Initialize archive object
$folderZip = new ZipArchive();
$folderZip->open($destination, ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Create recursive directory iterator
/** #var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file)
{
// Skip directories (they would be added automatically)
if (!$file->isDir())
{
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($source) + 1);
// Add current file to archive
$folderZip->addFile($filePath, $relativePath);
}
}
// Zip archive will be created only after closing object
$folderZip->close();
}
On it we can see two functions:
ZipFiles: is the main fucntion that is called by passing a list/array (contains the list of files and folders that will be added into the final ZIP) and an ID parameter which is simply used for generatig different file names... (can be ignored)
ZipFolder: this fucntion is called for each of the folders (not files) on the above mentioned list/array in order to zip that folder and create a zip file to add it on the final file. (based on what I found in How to zip a whole folder using PHP)
I have tried many things like mentioned in above post like closing all files, or avoiding empty zips inside the zip but nothing worked...
Zip inside zip (php)
Maybe I missed something (most probably :) ) but am running out of aces so any help/guideance would be appreciated.
In case more info is needed please let me know and will post it.
Thanks a lot in advance!!
Finally found the issue. Seems that the file was generated properly but when downloading it from PHP there was a problem when size was bigger than a concrete number.
THis was due to wrong definition of the message length on the header definition:
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Length: ".filesize($zippedFile));
header("Content-Disposition: attachment; filename=".$zippedFile);
header("Content-type: application/zip");
header("Content-Transfer-Encoding: binary");
Even if I guess it may not be a correct practice I removed the Content-Length entry and now I get the correct file despite of its size.
I am trying to zip a folder.
This is the function in ExportarController.php
public function zipping(){
$rootPath = realpath('results/');
// Initialize archive object
$zip = new \ZipArchive();
$zip->open('../web/descargas/Region.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Initialize empty "delete list"
$filesToDelete = array();
// Create recursive directory iterator
/** #var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file)
{
// Skip directories (they would be added automatically)
if (!$file->isDir())
{
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($rootPath) + 1);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
// Add current file to "delete list"
// delete it later cause ZipArchive create archive only after calling close function and ZipArchive lock files until archive created)
if ($file->getFilename() != 'important.txt')
{
$filesToDelete[] = $filePath;
}
}
}
// Zip archive will be created only after closing object
$zip->close();
// Delete all files from "delete list"
foreach ($filesToDelete as $file)
{
unlink($file);
}
}
It worked pefectly when I tried it outside yii2 webapp. But when I added the function to yii2 controller I got the error:
Class 'app\controllers\ZipArchive' not found
Do you know what is going wrong? does it have something to do with the directory I'm trying to write in?
Please help me.
Thank you so much.
SOLUTION
It was cuz of the namespace, I had to ad \ to every ziparchive function.
Like \ZipArchive() and \ZipArchive::CREATE | \ZipArchive::OVERWRITE
Thanks to all of you.
First enable php zip extension
$zip = new \ZipArchive();
$test = tempnam(sys_get_temp_dir(), rand(0, 999999999) . '.zip');
var_dump($test);
$res = $zip->open($test, \ZipArchive::CREATE);
if ($res) {
foreach ($csv_data as $data) {
$zip->addFile($data['file_name'],'chat_'. $data['chatID'] .'.csv');
}
$zip->close();
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename=chat_' . date('Ymd_His'). '.zip');
readfile($test);
} else {
echo 'zip error';
die;
}
Read for More : http://php.net/manual/en/class.ziparchive.php
To put it simply, I would like my users to be able to download their website files, so I created a "Download Website" button which uses this script to add all files/folders in their directory which is in the variable $direc and archive those files/folders.
<?
///////// DOWNLOAD ENTIRE WEBSITE:
if(isset($_POST['download_site'])){
// define some basics
$rootPath = '../useraccounts/'.$direc.'';
$archiveName = ''.$direc.'.zip';
// initialize the ZIP archive
$zip = new ZipArchive;
$zip->open($archiveName, ZipArchive::CREATE);
// create recursive directory iterator
$files = new RecursiveIteratorIterator (new RecursiveDirectoryIterator($rootPath), RecursiveIteratorIterator::LEAVES_ONLY);
// let's iterate
foreach ($files as $name => $file) {
$filePath = $file->getRealPath();
$zip->addFile($filePath);
}
// close the zip file
if (!$zip->close()) {
echo '<p>There was a problem writing the ZIP archive.</p>';
} else {
echo '<p>Successfully created the ZIP Archive!</p>';
}
}
?>
To my surprise, this code works. Although, there are a few hiccups:
It doesn't automatically force a download of that archive.
It adds the archive in to my main directory rather than moving it to a separate directory of my choice such as site_downloads or deletes it up on completed download.
Are these problems at all fixable or if not, is there a better way to do it so my main directory does not get filled with constant downloads? I guess it will cause a problem once a Archive is created more than once, as it uses the Directory name.
Solved this by using a few different combinations:
<?
///////// DOWNLOAD ENTIRE WEBSITE:
if(isset($_POST['download_site'])){
// define some basics
$rootPath = '../useraccounts/'.$direc.'';
$archiveName = ''.$direc.'.zip';
// initialize the ZIP archive
$zip = new ZipArchive;
$zip->open($archiveName, ZipArchive::CREATE);
// create recursive directory iterator
$files = new RecursiveIteratorIterator (new RecursiveDirectoryIterator($rootPath), RecursiveIteratorIterator::LEAVES_ONLY);
// let's iterate
foreach ($files as $name => $file) {
$filePath = $file->getRealPath();
$zip->addFile($filePath);
}
// close the zip file
if (!$zip->close()) {
echo '<p>There was a problem writing the ZIP archive.</p>';
} else {
rename($archiveName, 'user_archives/'.$archiveName.'');
$yourfile = "user_archives/".$archiveName."";
$file_name = basename($yourfile);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=$file_name");
header("Content-Length: " . filesize($yourfile));
readfile($yourfile);
ignore_user_abort(true);
if (connection_aborted()) {
unlink('user_archives/'.$archiveName.'');
} else {
unlink('user_archives/'.$archiveName.'');
}
echo '<p>Successfully created the ZIP Archive!</p>';
}
}
?>
This seems to fix all problems.
<?php
$error = "";
$file_folder = "temp/";
// folder to load files
if (extension_loaded('zip')) {
// Checking ZIP extension is available
$zip = new ZipArchive();
// Load zip library
$zip_name = "images_".date("d-m-Y") . ".zip";
// Zip name
if ($zip -> open($zip_name, ZIPARCHIVE::CREATE) !== TRUE) {
// Opening zip file to load files
$error .= "* Sorry ZIP creation failed at this time";
}
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator("../uploads/"));
$files = iterator_to_array($iterator, true);
foreach ($iterator as $file) {
$zip -> addFile($file_folder . $file);
// Adding files into zip
}
print_r($zip);
//echo $zip_name;
if (file_exists($file_folder.$zip_name)) {
// push to download the zip
header('Content-type: application/zip');
header('Content-Disposition: attachment; filename="' . $zip_name . '"');
readfile($zip_name);
// remove zip file is exists in temp path
unlink($zip_name);
} else {
echo "error";
}
$zip -> close();
//} else
// $error .= "* Please select file to zip ";
} else
$error .= "* You dont have ZIP extension";
?>
I have tried outputting $zip object and got following response:
ZipArchive Object ( [status] => 0 [statusSys] => 0 [numFiles] => 12 [filename] => /var/www/bigb/ajax/images_24-12-2012.zip [comment] => )
Status Zero explains there were no errors while creating zip (reference: http://www.php.net/manual/en/zip.constants.php)
I have referred posts:
1) Download all images from a single directory of a website
2) How to create a zip file using php and delete it after user downloads it?
3) http://www.9lessons.info/2012/06/creating-zip-file-with-php.html
Third one helped me the most and in the other 2 i was getting status as 23.
Issue: I am able to create a zip & no issue downloading it but when i open the zip i don't see any files inside but zip has memory in mbs.
Please Help me out...
Update: Error 23 occurs because of print_r / echo and or trying to overwrite same file.
Update2: Issue solved. It was due to path in RecursiveDirectoryIterator (i.e ../uploads/) once i moved the code to main folder and changed the path to (uploads/) everything started working as it should be. Kudos!!!
It was due to path in RecursiveDirectoryIterator (i.e ../uploads/) once i moved the code to main folder and changed the path to (uploads/) everything started working as it should be. Kudos!!!
I think the file path you are providing to $zip->addFile() is incorrect. You don't need to append anything to $file. Following will work:
foreach ($iterator as $file) {
$zip -> addFile($file);
}
In your case the $file will contain exact relative path of file like ../uploads/file.ext.