Hi,

I am trying to download multiple files using a zip file.
I want the php script to form a zip file after adding all the file from the folder that I want, then download that zip file.
I added an unlink function to make my zip file disappear, so the zip file downloaded will always be the one inside the folder.

Right now, by commenting the unlink, I can know that the zip folder is created inside the server. However there is nothing inside.
The downloaded zip file when I open, I see the message, the zip file folder is invalid. windows can't open the file ( I use a WAMP server). However, if I use 7zip to extract it, I can obtain and see the file, which is very confusing.

What am I missing? Is it the header?

echo"<form action='' method=POST enctype=multipart/form-data>";
echo"<input type=submit value='Export' name=downloadaszip class=downloadaszip>";
echo"</form>";
if(isset($_POST['downloadaszip']))
{
        //Directory
	$files=glob('.\\'.$_SESSION['folder'].'\cfile\*');
	$zip = new ZipArchive();
	//Open a ZIP file archive
	$zip->open('./user_'.$_SESSION['membername'].'/project.zip', ZipArchive::CREATE);
	//add each file to archive
	foreach ($files as $file) 
	{
		//Adds a file to a ZIP archive from the given path
		$zip->addFile($file);
	}
	//Close the active archive
	$zip->close();
	//identify browser type of file downloaded
	header('Content-Type: application/zip');
	//to produce download box and prompt user with project.zip
	header('Content-disposition: attachment; filename=project.zip');
	//download
	 header("Pragma: no-cache");
  header("Expires: 0");

readfile("./user_".$_SESSION['membername']."/project.zip");
    //unlink('./'.$_SESSION['folder'].'/project.zip');
}

Thanks in advance.

    Because you are adding files such as C:\path\to\file.ext you should instead use zip->addFile($file,$target) where target could be everything but the drive path like path\to\file.ext. As it stands now windows doesn't understand the file paths within the zip file. I ran into this problem before.

      So I need to change

      $zip->addFile($file); 

      to

       zip->addFile($file,$target) 

      Is that correct?
      If it is, can you explain more on what you mean by

      target could be everything but the drive path like path\to\file.ext

      Do you mean that I should insert

      $target="project.zip";
      

      The error messages still appear.

        I meant cut off the beginning of the path, so if you are adding folderx I would do something like:

        $dir = 'C:\\path\\to\\folderx\\';
        $files = glob($dir.'*');
        $base = realpath($dir);
        foreach( $files as $file ) {
           $target = str_replace($base,'',$file);
           $zip->addFile($file,$target);
        }

        Hope that helps. When windows opens the zip and sees the first level of storage as C:\ it thinks its invalid. So you could even just strip off the drive letter.

          I try this code

          echo"<form action='' method=POST enctype=multipart/form-data>";
          echo"<input type=submit value='Export' name=downloadaszip class=downloadaszip>";
          echo"</form>";
          if(isset($_POST['downloadaszip']))
          {
              //Directory
          	$dir='.\\'.$_SESSION['folder'].'\cfile\\';
          	$files=glob($dir.'*');
          	$base=realpath($dir);
          	$zip = new ZipArchive();
          	//Open a ZIP file archive
          	$zip->open('./user_'.$_SESSION['membername'].'/project.zip', ZipArchive::CREATE);
          	//add each file to archive
          	foreach ($files as $file) 
          	{
          		$target=str_replace($base,'',$file);
          		//Adds a file to a ZIP archive from the given path
          		$zip->addFile($file, $target);
          	}
          	//Close the active archive
          	$zip->close();
          	//identify browser type of file downloaded
          	header('Content-Type: application/zip');
          	//to produce download box and prompt user with project.zip
          	header('Content-disposition: attachment; filename=project.zip');
          	//download
          	//header("Pragma: no-cache");
          	header("Expires: 0");
          	header("Pragma: "); header("Cache-Control: ");
          	readfile("./user_".$_SESSION['membername']."/project.zip");
                  //unlink('./'.$_SESSION['folder'].'/project.zip');
          }

          The current code still result in a downloaded file which when I open still produce an invalid error message.
          The project.zip is created in the directory in the server that I specify. Inside the zip file in the server another project.zip is created and it is invalid too.

            Well I don't know exactly what to tell you, have you tried removing the headers and adding error checking and output:

            	echo "<pre>";
            	$zip = new ZipArchive(); 
            	//Open a ZIP file archive 1
            	if( $zip->open('./user_'.$_SESSION['membername'].'/project.zip', ZipArchive::CREATE) ) {
            		echo "Zip created successfully\n";
            	} else {
            		die('Unable to create zip');
            	}
            	//add each file to archive 
            	foreach ($files as $file) 
            	{ 
            		$target=str_replace($base,'',$file); 
            		//Adds a file to a ZIP archive from the given path 
            		if( $zip->addFile($file, $target) ) {
            			echo "$file added as $target succesfully\n";
            		} else {
            			echo "$file was unable to be added\n";
            		}
            	} 
            	//Close the active archive 
            	if( $zip->close() ) {
            		echo "Succesfully close archive\n";
            	} else {
            		echo "Unable to close archive, errors may have occured\n";
            	}
            	echo '</pre>';

            Doing that and then just outputting a link to the archive might be better for the time being until you get the zip created successfully.

            Also here is a script I use to back up my websites, hopefully it can help you understand what I do to the filepath?

            <?php
            
            // Settings
            $flags = array(
               'FTP' => TRUE,
            );
            // Directory
            $folderBlacklist = array('cgi-bin');
            $dir = '/path/to/folder';
            
            // FTP
            $FTPHOST = '--';
            $FTPUSER = '--';
            $FTPPASS = '--';
            
            
            
            
            /******************************\
             **** Don't Edit Below HERE ***
            \******************************/
            
            
            // Initialize zip file
            $zipname = date('Y-m-d').'_'.$_SERVER['SERVER_NAME'].'.zip';
            if( file_exists($zipname) ) unlink($zipname);
            
            $zip = new ZipArchive();
            $zip->open($zipname,ZipArchive::CREATE);
            
            addToZip($dir,$zip);
            
            if( $zip->close() === FALSE ) {
               print("\nArchive creation failed.\n");
            } else {
               print("\nArchive creation successful.\n");
            }
            
            if( $flags['FTP'] ) {
               if( $ftp = ftp_connect($FTPHOST) ) {
                  if( ftp_login($ftp,$FTPUSER,$FTPPASS) ) {
                     ftp_pasv($ftp,TRUE);
                     if( ftp_put($ftp,$zipname,basename($zipname),FTP_BINARY) ) {
                        echo "\n\nFile Upload successful.";
                        unlink($zipname);
                     } else {
                        echo "\n\nFile Upload failed.";
                     }
                  } else {
                     echo "\n\nFTP login failed.";
                  }
                  ftp_close($ftp);
               } else {
                  echo "\n\nFTP connection failed.";
               }
            }
            
            function addToZip($path,ZipArchive $zip) {
               static $depth = 0;
               static $root;
            
               if( $depth == 0 && is_dir($path) ) {
                  $root = realpath($path) . DIRECTORY_SEPARATOR;
                  $depth++;
               }
            
               if( is_file($path) ) {
                  $file = str_replace($root,'',$path);
                  if( $zip->addFile($path,$file) ) {
                     echo "Added $path as $file\n";
                  } else {
                     echo "FAILED $path\n";
                  }
               } elseif( is_dir($path) ) {
                  echo "\nAdding folder: $path\n";
            
              $dir = realpath($path) . DIRECTORY_SEPARATOR;
              $dir = glob($dir.'*');
              array_walk($dir,'filterGlob');
              $dir = array_filter($dir);
              usort($dir,'sortGlob');
            
              foreach( $dir as $sub ) {
                 addToZip($sub,$zip);
              }
               } else {
                  return FALSE;
               }
            }
            
            function sortGlob($a,$b) {
               if( is_dir($a) && is_file($b) ) return -1;
               if( is_file($a) && is_dir($b) ) return 1;
               return strcasecmp($a,$b); 
            }
            function filterGlob(&$path,$key) {
               GLOBAL $folderBlacklist;
               if( in_array(basename($path),$folderBlacklist) )
                  $path = FALSE;
            }

              I think that there are certain fundamental problems with the code that you are both overlooking:
              1. double slashes in $dir='.\'.$_SESSION['folder'].'\cfile\';
              2. no filter of directories in $files=glob($dir.'*');
              3. no creation of sub-directories in the archive when glob has returned a file path with a sub-directory

              I would echo out the $dir and print_r($files) first to see if the basics are correct. After that you can move on to the next step in debugging this.

                Roger Ramjet;11003974 wrote:

                I think that there are certain fundamental problems with the code that you are both overlooking:
                1. double slashes in $dir='.\'.$_SESSION['folder'].'\cfile\';

                Double slashes are necessary to have the quote close the string. Try it out:

                'this string is not closed, because only one slash \'
                'this string is closed, because 2 slashes \\'

                2. no filter of directories in $files=glob($dir.'*');
                3. no creation of sub-directories in the archive when glob has returned a file path with a sub-directory

                Not sure what you mean here, but it was my understanding from his code that the glob should only be returning files. However if the glob is returning folders, then this definitely needs to be addressed.

                I would echo out the $dir and print_r($files) first to see if the basics are correct. After that you can move on to the next step in debugging this.

                Agreed this would be a good step to help.

                  Yes, you're right about the escape slash, my bad.

                  Glob(): "Returns an array containing the matched files/directories, an empty array if no file matched or FALSE on error."

                  Since he is using * to match all then . , .. , and any subdirs will be returned.

                    No, try using [man]glob[/man]... it doesn't return . or .. you're thinking of [man]readdir[/man]

                    echo '<pre>'.print_r(glob(__DIR__.DIRECTORY_SEPARATOR.'*'),true).'</pre>';

                    Edit: Not trying to be rude Roger, just don't want him to spend time debugging an issue that doesn't exist. =D

                      I never said it didn't show sub dirs brad: [emphasis added]

                      Derokorian;11003985 wrote:

                      No, try using [man]glob[/man]... it doesn't return . or ..

                        Write a Reply...