Keywords: PHP | ZIP files | download errors | HTTP headers | ZipArchive class
Abstract: This article provides an in-depth analysis of the 'End-of-central-directory signature not found' error encountered when creating and downloading ZIP files using PHP's ZipArchive class. By examining issues in the original code, particularly the lack of Content-length headers and whitespace before output, it offers comprehensive solutions. The paper explains the structural principles of ZIP file format, the importance of HTTP header configuration, and presents optimized code examples to ensure generated ZIP files can be properly extracted.
Problem Background and Error Analysis
In web development, there is often a need to package multiple files into ZIP format and provide download functionality. PHP's ZipArchive class offers a convenient interface for this purpose, but developers may encounter issues with corrupted or unextractable ZIP files. Typical error messages include: 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. This error usually indicates that the generated ZIP file has an incomplete structure or format issues.
Diagnosis of Original Code Issues
Examining the original code from the Q&A, while basic functionality appears correct, two critical flaws exist:
- Missing Content-length Header: The HTTP response does not specify the size of the ZIP file, which may prevent clients (such as browsers or extraction tools) from correctly reading the central directory structure at the file's end.
- Whitespace Before Output: A space before the
<?phptag causes whitespace to be output as part of the response, disrupting the binary structure of the ZIP file.
The ZIP file format requires complete central directory records and end markers at the file's conclusion. Any additional output disrupts this structure, preventing extraction tools from locating proper file boundaries.
Solutions and Code Optimization
Based on the best answer's guidance, here is a corrected complete code example:
<?php
function createAndDownloadZip($fileNames, $archiveName, $filePath) {
$zip = new ZipArchive();
if ($zip->open($archiveName, ZipArchive::CREATE) !== true) {
exit("Cannot create ZIP file: " . $archiveName);
}
foreach ($fileNames as $fileName) {
$fullPath = $filePath . $fileName;
if (file_exists($fullPath)) {
$zip->addFile($fullPath, $fileName);
} else {
// Handle missing files
error_log("File not found: " . $fullPath);
}
}
$zip->close();
// Set proper HTTP headers
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . $archiveName);
header("Content-Length: " . filesize($archiveName));
header("Cache-Control: no-cache, must-revalidate");
header("Expires: 0");
readfile($archiveName);
// Optional: Delete temporary file after download
unlink($archiveName);
exit;
}
// Usage example
$fileNames = ['iMUST Operating Manual V1.3a.pdf', 'iMUST Product Information Sheet.pdf'];
$archiveName = 'iMUST_Products.zip';
$filePath = $_SERVER['DOCUMENT_ROOT'] . '/Harshal/files/';
createAndDownloadZip($fileNames, $archiveName, $filePath);
?>
Detailed Technical Points
1. Importance of Content-Length Header
In HTTP responses, the Content-Length header informs the client of the exact size of the response body. For binary files like ZIP, this information is crucial. Without it, clients may be unable to determine the file's end, leading to incomplete or erroneous reads. The filesize() function accurately retrieves the ZIP file's size.
2. Strictness of Output Control
PHP scripts must ensure no extra output precedes binary content. This includes:
- Eliminating all whitespace before the
<?phptag - Avoiding debug output (e.g., echo, print statements) during script execution
- Ensuring no BOM (Byte Order Mark) or other invisible characters are present
Functions like ob_clean() or ob_start() with ob_end_clean() can ensure a clean output buffer.
3. ZIP File Structure Integrity Verification
During development, validate generated ZIP files using:
<?php
// Verify if ZIP file is valid
function validateZipFile($filename) {
$zip = new ZipArchive();
$result = $zip->open($filename);
if ($result === true) {
$zip->close();
return true;
}
return false;
}
?>
Best Practice Recommendations
1. Error Handling: Always check if files exist and are readable, providing meaningful error messages.
2. Memory Management: For large files, consider streaming or chunked reading to avoid memory overflow.
3. Security: Validate user-input file paths to prevent directory traversal attacks.
4. Compatibility: Ensure the ZipArchive extension is enabled on the server and test compatibility across PHP versions.
Conclusion
When creating downloadable ZIP files, details determine success. By correctly setting HTTP headers, strictly controlling output, and adhering to ZIP file format specifications, common "End-of-central-directory" errors can be avoided. The solutions provided in this article not only fix issues in the original code but also add robust error handling and file validation mechanisms, offering reliable reference implementations for practical project development.