問題
nginx を使ったwebサービスで、メガバイト級のファイル群をzipファイルにまとめてダウンロードする、という機能をつくる際に困った問題が起きました。
phpをつかってるので、ZipArchiveを使ってzipを生成するようにしてます。
zip生成 & zipダウンロードのコード
$filepath = [..., ..., ...];
$zipName = '/var/www/html/example.zip';
if (!file_exists($zipName)) {
mkdir($zipTmpPath, 0777, TRUE);
}
$zip = new ZipArchive();
$zipResult = $zip->open($zipName, ZipArchive::CREATE | ZipArchive::OVERWRITE);
if ($zipResult !== true) {
throw new Exception("Zip open is FALSE");
}
try {
foreach ($files as $filepath) {
$filename = basename($filepath);
if ($zip->addFile($filepath, $filename) === FALSE) {
if (!$zip->close()) {
throw new Exception("$zip->addFile is FALSE AND $zip->close() is FALSE");
} else {
throw new Exception("$zip->addFile is FALSE");
}
}
}
if (!$zip->close()) {
throw new Exception("$zip->close() is FALSE");
}
} catch (Exception $e) {
throw new Exception("zip exception");
}
if (!file_exists($zipPath)) {
throw new Exception('Zip does not exist.');
}
header('Content-Type: application/force-download');
header('Content-Length: ' . filesize($zipName));
header('Content-Disposition: attachment; filename="' . basename($zipName) . '"');
while (ob_get_level() > 0) {
ob_end_clean();
}
ob_start();
if ($realFile = fopen($zipName, 'rb')) {
while (!feof($realFile) and (connection_status() == 0)) {
echo fread($realFile, '4096');
ob_flush();
}
ob_flush();
fclose($realFile);
}
ob_end_clean();
unlink($zipName);
※ 参考コード: https://blog.kuromusubi.com/develop/language/php/20180114-ziparchive
問題の詳細
- 400メガバイト程度のファイル群をzipにしようとすると、zipArchiveの
addFile
部分で処理が止まってしまう。 - 上記の場合、ブラウザ上では 「404 not found nginx」 と表示されてしまう。
nginxのerror.log
には、以下のエラー内容が出力されていた。
upstream timed out (110: Connection timed out) while reading response header from upstream, client: ....
open() "/var/www/html/dev/50x.html" failed (2: No such file or directory),...
処理が止まっていた要因
- メモリ不足
- phpのタイムアウト
- nginxのタイムアウト
対策
メモリ不足の対策
/etc/php.ini
memory_limit = 256M
phpのタイムアウトの対策
/etc/php.ini
max_execution_time = 600
nginxのタイムアウトの対策
/etc/nginx/nginx.conf
fastcgi_read_timeout 1200;