43

I am trying to download a 2 files by creating the zip file on local-server.the file is downloaded in zip format but when i try to extract it.it gives error: End-of-central-directory signature not found. Either this file is not a zip file, or it constitutes one disk of a multi-part archive. In the latter case the central directory and zip file comment will be found on the last disk(s) of this archive.

the following code i am using for this:

 <?php
$file_names = array('iMUST Operating Manual V1.3a.pdf','iMUST Product Information Sheet.pdf');

//Archive name
$archive_file_name=$name.'iMUST_Products.zip';

//Download Files path
$file_path=$_SERVER['DOCUMENT_ROOT'].'/Harshal/files/';


zipFilesAndDownload($file_names,$archive_file_name,$file_path);

function zipFilesAndDownload($file_names,$archive_file_name,$file_path)
{
        //echo $file_path;die;
    $zip = new ZipArchive();
    //create the file and throw the error if unsuccessful
    if ($zip->open($archive_file_name, ZIPARCHIVE::CREATE )!==TRUE) {
        exit("cannot open <$archive_file_name>\n");
    }
    //add each files of $file_name array to archive
    foreach($file_names as $files)
    {
        $zip->addFile($file_path.$files,$files);
        //echo $file_path.$files,$files."

    }
    $zip->close();
    //then send the headers to force download the zip file
    header("Content-type: application/zip"); 
    header("Content-Disposition: attachment; filename=$archive_file_name"); 
    header("Pragma: no-cache"); 
    header("Expires: 0"); 
    readfile("$archive_file_name");
    exit;
}




?>

i checked the values of all variables which are passing into the function,all are fine.so please look this.Thanks in advance.

Legionar
  • 7,472
  • 2
  • 41
  • 70
Harshal
  • 3,562
  • 9
  • 36
  • 65
  • Have you checked, whether the ZIP file on your server machine is valid? Also, you may do well to erase the ZIP file once you're done with it, because otherwise it will clutter your server HD. – ATaylor Sep 01 '12 at 07:56
  • @ATaylor,i am running it on local-server. – Harshal Sep 01 '12 at 07:58
  • You have a space at the beginning of your file, before the ` – David Schwartz Sep 01 '12 at 08:17
  • @david,i removed those spaces but still giving same error. – Harshal Sep 01 '12 at 08:26
  • Please refer this answers [http://stackoverflow.com/questions/5603851/how-to-create-a-zip-file-using-php-and-delete-it-after-user-downloads-it/17399319#17399319][1] [1]: http://stackoverflow.com/questions/5603851/how-to-create-a-zip-file-using-php-and-delete-it-after-user-downloads-it/17399319#17399319 – V A S Jul 01 '13 at 07:31

8 Answers8

58

Add Content-length header describing size of zip file in bytes.

header("Content-type: application/zip"); 
header("Content-Disposition: attachment; filename=$archive_file_name");
header("Content-length: " . filesize($archive_file_name));
header("Pragma: no-cache"); 
header("Expires: 0"); 
readfile("$archive_file_name");

Also make sure that there is absolutely no white space before <? and after ?>. I see a space here:

 <?php
$file_names = array('iMUST Operating Manual V1.3a.pdf','iMUST Product Information Sheet.pdf');
Kuba Wyrostek
  • 6,163
  • 1
  • 22
  • 40
  • Content-length shouldn't be necessary, the server should automatically use chunked encoding. – Barmar Sep 01 '12 at 08:12
  • 1
    @kuba, not working..!!and there is no space in my opening and closing tags. – Harshal Sep 01 '12 at 08:16
  • Can you compare ZIP files on server-side and client-side? Are they identical? – Kuba Wyrostek Sep 01 '12 at 08:17
  • 1
    yes ,on server side the length of zip file is 226 bytes,and on client side its 1.2 mb.if there is some matter of size than plz give me the exact code for the size of content-length – Harshal Sep 01 '12 at 08:20
  • server side 226 bytes and client 1.2mb? not the other way round?? – Kuba Wyrostek Sep 01 '12 at 08:32
  • i just check on local by compressing the files,and it gives size of 1.2 mb,but the file i am getting from server after download it gives the size of 226 bytes. – Harshal Sep 01 '12 at 10:34
  • OK, please try with `Content-length`. I provided an example. – Kuba Wyrostek Sep 01 '12 at 10:38
  • so now it gives the zip file of length 420 bytes,but still giving same error. – Harshal Sep 01 '12 at 11:04
  • OK, it seems to me, that ZIP file isn't completely written to disk before accessed with `readfile()`. Please add, for the sake of testing only, `sleep(5);` before `readfile("$archive_file_name");`. – Kuba Wyrostek Sep 01 '12 at 11:17
  • @KubaWyrostek, that is very unlikely: `$zip` functions are disk synchronous, there is no concurrent process performing `addFile`. – LSerni Sep 01 '12 at 11:29
  • I am just trying to help. It doesn't cost much to check `sleep()`. :] – Kuba Wyrostek Sep 01 '12 at 11:30
  • @kuba ,still giving same size of files. – Harshal Sep 01 '12 at 12:59
  • sorry, I'm out of hints then... maybe consider using different zip handling library. – Kuba Wyrostek Sep 01 '12 at 13:01
  • So, the code needs some basic syntax revisions: header("Content-Disposition: attachment; filename=$archive_file_name"); needs to be header("Content-Disposition: attachment; filename=".$archive_file_name); and readfile("$archive_file_name"); needs to be readfile($archive_file_name); – sdailey Dec 15 '12 at 17:46
  • 3
    No it does not. Please consult http://php.net/manual/en/language.types.string.php Variables are substituted in strings and I didn't want to change original code (not to confuse OP). – Kuba Wyrostek Dec 15 '12 at 21:53
  • The solution still is the correct approach, however I suggest using single apostrophes instead of double, it makes it easier to read. –  Jul 07 '21 at 07:11
13
$zip = new ZipArchive;
$tmp_file = 'assets/myzip.zip';
    if ($zip->open($tmp_file,  ZipArchive::CREATE)) {
        $zip->addFile('folder/bootstrap.js', 'bootstrap.js');
        $zip->addFile('folder/bootstrap.min.js', 'bootstrap.min.js');
        $zip->close();
        echo 'Archive created!';
        header('Content-disposition: attachment; filename=files.zip');
        header('Content-type: application/zip');
        readfile($tmp_file);
   } else {
       echo 'Failed!';
   }
Apps-n-Add-Ons
  • 2,026
  • 1
  • 17
  • 28
M Arfan
  • 4,384
  • 4
  • 29
  • 46
  • Thanks a lot! and this works for me, because I have missing the exact path for the temp file. and my error was this ErrorException-ZipArchive::close(): Failure to create temporary file: Permission denied – Jesus Erwin Suarez Aug 05 '20 at 05:08
7
// http headers for zip downloads
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filepath.$filename));
ob_end_flush();
@readfile($filepath.$filename);

I found this soludtion here and it work for me

vitams
  • 585
  • 6
  • 7
6

One of the error could be that the file is not read as 'archive' format. check out ZipArchive not opening file - Error Code: 19. Open the downloaded file in text editor, if you have any html tags or debug statements at the starting, clear the buffer before reading the file.

ob_clean();
flush();
readfile("$archive_file_name");
Community
  • 1
  • 1
Avinav
  • 542
  • 5
  • 7
5

I just ran into this problem. For me the issue was with:

readfile("$archive_file_name");

It was resulting in a out of memory error.

Allowed memory size of 134217728 bytes exhausted (tried to allocate 292982784 bytes)

I was able to correct the problem by replacing readfile() with the following:

    $handle = fopen($zipPath, "rb");
    while (!feof($handle)){
        echo fread($handle, 8192);
    }
    fclose($handle);

Not sure if this is your same issue or not seeing that your file is only 1.2 MB. Maybe this will help someone else with a similar problem.

David Rodriguez
  • 133
  • 1
  • 6
1

I have experienced exactly the same problem. In my case, the source of it was the permissions of the folder in which I wanted to create the zip file that were all set to read only. I changed it to read and write and it worked.

If the file is not created on your local-server when you run the script, you most probably have the same problem as I did.

0

but the file i am getting from server after download it gives the size of 226 bytes

This is the size of a ZIP header. Apparently there is no data in the downloaded ZIP file. So, can you verify that the files to be added into the ZIP file are, indeed, there (relative to the path of the download PHP script)?

Consider adding a check on addFile too:

foreach($file_names as $file)
{
    $inputFile = $file_path . $file;
    if (!file_exists($inputFile))
        trigger_error("The input file $inputFile does not exist", E_USER_ERROR);
    if (!is_readable($inputFile))
        trigger_error("The input file $inputFile exists, but has wrong permissions or ownership", E_USER_ERROR);
    if (!$zip->addFile($inputFile, $file))
        trigger_error("Could not add $inputFile to ZIP file", E_USER_ERROR);
}

The observed behaviour is consistent with some problem (path error, permission problems, ...) preventing the files from being added to the ZIP file. On receiving an "empty" ZIP file, the client issues an error referring to the ZIP central directory missing (the actual error being that there is no directory, and no files).

LSerni
  • 55,617
  • 10
  • 65
  • 107
  • Thanks for answer iserni, by putting your code i am getting this error: Fatal error: Could not add /opt/lampp/htdocs/Harshal/files/iMUST Operating Manual V1.3a.pdf as iMUST Operating Manual V1.3a.pdf from script in /opt/lampp/htdocs/Harshal in /opt/lampp/htdocs/Harshal/ex_sub.php on line 25 – Harshal Sep 01 '12 at 11:08
  • I have modified the code to try to better track the error. Looks like a file naming problem, path problem, or permission error. – LSerni Sep 01 '12 at 11:26
  • now its giving: Fatal error: The input file /opt/lampp/htdocs/Harshal/files/iMUST Operating Manual V1.3a.pdf does not exist in /opt/lampp/htdocs/Harshal/ex_sub.php on line 27 – Harshal Sep 01 '12 at 11:39
  • i am sure there is a files ,but wondered why this error comes – Harshal Sep 01 '12 at 11:40
  • Check the contents of the `/opt/lampp/htdocs/Harshal/files/` directory (and the permissions on `Harshal` and `Harshal/files` too). The file may be there, but PHP believes otherwise. – LSerni Sep 01 '12 at 12:08
  • i already given the permission to Harshal/files.but the same error occurs. – Harshal Sep 01 '12 at 12:55
0

Make sure that the path inside $archive_file_name is correct and does exist. In your code, you updated the $archive_file_name variable by putting an undeclared prefix variable - $name.

$archive_file_name = $name.'iMUST_Products.zip';

If the path is not correct and cannot be found, the created zipped file have nowhere to go to and the two files have nowhere to store to to begin with. The path to the files to be stored should also exist.

Let's assume that you just want to customize the name of the zipped folder to be downloaded that is why there is a $name variable to prevent redundant file name. But you have to consider the URI where you run your script.

For example, you're using URI routing, and you load the script at https://sample.co/file-management/download-zipped-111. With your current script, the zipped folder will be stored at file-management folder. If it does not exist, you will mostly encounter the error you are experiencing right now.

The solution is to explicitly declare the path to the folder where the zipped file will be stored.

For example, you want to store the zipped file at Harshal/files/zipped/. Make sure that this folder exist by creating a folder named zipped inside files folder and that you have access to write on this folder.

$file_names = array('iMUST Operating Manual V1.3a.pdf', 'iMUST Product Information Sheet.pdf');

// $name should be declared before this line
$archive_file_name = $name.'iMUST_Products.zip';

//Download Files path
$file_path = $_SERVER['DOCUMENT_ROOT'].'/Harshal/files/';

zipFilesAndDownload($file_names, $archive_file_name, $file_path);

function zipFilesAndDownload($file_names, $archive_file_name, $file_path)
{

    $zip = new ZipArchive();
    // WE REUSED THE $file_path VARIABLE HERE THEN ADDED zipped FOLDER
    if ($zip->open($file_path.'zipped/'.$archive_file_name, ZIPARCHIVE::CREATE )!==TRUE) {
        exit('cannot open <'.$archive_file_name.'>');
    }
    //add each files of $file_name array to archive
    foreach($file_names as $files)
    {
        $zip->addFile($file_path.$files, $files);
    }
    $zip->close();
    //then send the headers to force download the zip file
    header("Content-type: application/zip"); 
    header("Content-Disposition: attachment; filename=$archive_file_name"); 
    header('Content-length: '.filesize($file_path.'zipped/'.$archive_file_name));
    header("Pragma: no-cache"); 
    header("Expires: 0"); 
    readfile("$archive_file_name");
    exit;
}

People may suggest - "Why not directly put the full path to the $archive_file_name variable?

$archive_file_name = $file_path.'zipped/'.$name.'iMUST_Products.zip';

If we put it directly to $archive_file_name variable, the file that will be downloaded will mostly likely be named /var/www/Harshal/files/zipped/xxx-iMUST_Products.zip.

Logan Wayne
  • 6,001
  • 16
  • 31
  • 49