|
@@ -10,7 +10,6 @@ use App\Tools\CommonService;
|
|
use App\Tools\PublicData;
|
|
use App\Tools\PublicData;
|
|
use App\Tools\Result;
|
|
use App\Tools\Result;
|
|
use function Hyperf\Support\env;
|
|
use function Hyperf\Support\env;
|
|
-use function Hyperf\Coroutine\parallel;
|
|
|
|
use Hyperf\Context\Context;
|
|
use Hyperf\Context\Context;
|
|
use Hyperf\Di\Annotation\Inject;
|
|
use Hyperf\Di\Annotation\Inject;
|
|
use Hyperf\HttpServer\Contract\RequestInterface;
|
|
use Hyperf\HttpServer\Contract\RequestInterface;
|
|
@@ -352,8 +351,8 @@ class PublicController extends AbstractController
|
|
// 放开执行时限,避免大量文件导致的超时
|
|
// 放开执行时限,避免大量文件导致的超时
|
|
ignore_user_abort(true);
|
|
ignore_user_abort(true);
|
|
@set_time_limit(0);
|
|
@set_time_limit(0);
|
|
-
|
|
|
|
- // 验证参数
|
|
|
|
|
|
+
|
|
|
|
+ //验证是否有传参数
|
|
$requireData = $this->request->all();
|
|
$requireData = $this->request->all();
|
|
$validator = $this->validationFactory->make(
|
|
$validator = $this->validationFactory->make(
|
|
$requireData,
|
|
$requireData,
|
|
@@ -368,156 +367,252 @@ class PublicController extends AbstractController
|
|
$errorMessage = $validator->errors()->first();
|
|
$errorMessage = $validator->errors()->first();
|
|
return Result::error($errorMessage);
|
|
return Result::error($errorMessage);
|
|
}
|
|
}
|
|
-
|
|
|
|
$requireData['fileName'] = $requireData['fileName'] ?? '政讯通';
|
|
$requireData['fileName'] = $requireData['fileName'] ?? '政讯通';
|
|
-
|
|
|
|
- // 归一化文件与名称
|
|
|
|
- /** @var array<int|string, string> $files */
|
|
|
|
- $files = [];
|
|
|
|
- if (is_array($requireData['files'])) {
|
|
|
|
- // 保留原有索引,避免与 names 对应关系错位
|
|
|
|
- $files = array_filter($requireData['files'], fn($v) => is_string($v) && $v !== '');
|
|
|
|
- } elseif (is_string($requireData['files'])) {
|
|
|
|
- $files = [$requireData['files']];
|
|
|
|
- }
|
|
|
|
- $names = [];
|
|
|
|
- if (isset($requireData['names']) && is_array($requireData['names'])) {
|
|
|
|
- $names = $requireData['names'];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (count($files) === 0) {
|
|
|
|
- return Result::error('文件不能为空');
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 预下载远程文件(并发)
|
|
|
|
- $remoteIndexes = [];
|
|
|
|
- foreach ($files as $index => $path) {
|
|
|
|
- if (is_string($path) && filter_var($path, FILTER_VALIDATE_URL)) {
|
|
|
|
- $remoteIndexes[] = $index;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- $tempFiles = [];
|
|
|
|
- if (!empty($remoteIndexes)) {
|
|
|
|
- $concurrency = (int) env('ZIP_DOWNLOAD_CONCURRENCY', 5);
|
|
|
|
- $tasks = [];
|
|
|
|
- foreach ($remoteIndexes as $idx) {
|
|
|
|
- $url = $files[$idx];
|
|
|
|
- $tasks[] = function () use ($idx, $url) {
|
|
|
|
- $temp = $this->downloadRemoteToTemp($url);
|
|
|
|
- return [$idx, $temp];
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- try {
|
|
|
|
- $results = parallel($concurrency, $tasks);
|
|
|
|
- foreach ($results as $result) {
|
|
|
|
- if (is_array($result) && count($result) === 2) {
|
|
|
|
- [$i, $temp] = $result;
|
|
|
|
- if (is_string($temp) && $temp !== '' && is_file($temp)) {
|
|
|
|
- $tempFiles[$i] = $temp;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } catch (\Throwable $e) {
|
|
|
|
- // 并发下载异常不终止,尽量继续处理其他文件
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 创建 ZIP 目录与文件
|
|
|
|
|
|
+ //打包文件
|
|
$zip = new ZipArchive();
|
|
$zip = new ZipArchive();
|
|
$fileType = 'zip';
|
|
$fileType = 'zip';
|
|
$date = date('Ymd');
|
|
$date = date('Ymd');
|
|
$filePath = $fileType . DIRECTORY_SEPARATOR . $date;
|
|
$filePath = $fileType . DIRECTORY_SEPARATOR . $date;
|
|
- $allDir = 'public' . DIRECTORY_SEPARATOR . $filePath;
|
|
|
|
- if (!is_dir($allDir) && !mkdir($allDir, 0755, true) && !is_dir($allDir)) {
|
|
|
|
- // 清理已下载的临时文件
|
|
|
|
- foreach ($tempFiles as $temp) {
|
|
|
|
- @unlink($temp);
|
|
|
|
|
|
+ // 使用绝对路径并规范化
|
|
|
|
+ $allDir = BASE_PATH . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . $filePath;
|
|
|
|
+ $allDir = realpath($allDir) ?: $allDir;
|
|
|
|
+
|
|
|
|
+ // 打印目录路径以便调试
|
|
|
|
+ error_log("DownloadFile: Full directory path: $allDir");
|
|
|
|
+
|
|
|
|
+ if (!is_dir($allDir)) {
|
|
|
|
+ if (!mkdir($allDir, 0755, true)) {
|
|
|
|
+ error_log("DownloadFile: Failed to create directory: $allDir");
|
|
|
|
+ return Result::error('创建文件夹失败');
|
|
}
|
|
}
|
|
- return Result::error('创建文件夹失败');
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+ // 生成绝对路径的ZIP文件名
|
|
$zipFileName = $allDir . DIRECTORY_SEPARATOR . $requireData['fileName'] . time() . mt_rand(1, 1000000) . '.zip';
|
|
$zipFileName = $allDir . DIRECTORY_SEPARATOR . $requireData['fileName'] . time() . mt_rand(1, 1000000) . '.zip';
|
|
-
|
|
|
|
- $addedAny = false;
|
|
|
|
- $zipOpened = false;
|
|
|
|
- try {
|
|
|
|
- if ($zip->open($zipFileName, ZipArchive::CREATE) !== true) {
|
|
|
|
- return Result::error('无法创建zip文件');
|
|
|
|
|
|
+ error_log("DownloadFile: ZIP file path: $zipFileName");
|
|
|
|
+ error_log("DownloadFile: Directory exists: " . (is_dir($allDir) ? 'yes' : 'no'));
|
|
|
|
+ error_log("DownloadFile: Directory writable: " . (is_writable($allDir) ? 'yes' : 'no'));
|
|
|
|
+
|
|
|
|
+ // 添加OVERWRITE标志确保文件可以被创建
|
|
|
|
+ if ($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
|
|
|
|
+ // 将要下载的文件逐个添加到zip文件中
|
|
|
|
+ /** @var array<string> $files */
|
|
|
|
+ $files = [];
|
|
|
|
+ if (is_array($requireData['files'])) {
|
|
|
|
+ $files = $requireData['files'];
|
|
|
|
+ } elseif (is_string($requireData['files'])) {
|
|
|
|
+ $files = [$requireData['files']];
|
|
}
|
|
}
|
|
- $zipOpened = true;
|
|
|
|
-
|
|
|
|
- foreach ($files as $index => $originalPath) {
|
|
|
|
- if (!is_string($originalPath) || $originalPath === '') {
|
|
|
|
|
|
+
|
|
|
|
+ // 添加进度日志
|
|
|
|
+ error_log("DownloadFile: Processing " . count($files) . " files");
|
|
|
|
+
|
|
|
|
+ $addedCount = 0;
|
|
|
|
+ $failedCount = 0;
|
|
|
|
+
|
|
|
|
+ foreach ($files as $key => $filePathu) {
|
|
|
|
+ if (!is_string($filePathu)) {
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 决定压缩包内的文件名
|
|
|
|
- $zipInnerName = $names[$index] ?? '';
|
|
|
|
- if (!is_string($zipInnerName) || $zipInnerName === '') {
|
|
|
|
- if (filter_var($originalPath, FILTER_VALIDATE_URL)) {
|
|
|
|
- $zipInnerName = basename(parse_url($originalPath, PHP_URL_PATH) ?: ('file_' . ($index + 1) . $this->getFileExtension($originalPath)));
|
|
|
|
- } else {
|
|
|
|
- $zipInnerName = basename($originalPath);
|
|
|
|
- if ($zipInnerName === '' || $zipInnerName === '.' || $zipInnerName === '..') {
|
|
|
|
- $zipInnerName = 'file_' . ($index + 1) . $this->getFileExtension($originalPath);
|
|
|
|
|
|
+
|
|
|
|
+ // 生成文件名:key + 1
|
|
|
|
+ $innerFileName = $requireData['names'][$key] ?? 'file_' . ($key + 1); //($key + 1) . $this->getFileExtension($filePathu);
|
|
|
|
+
|
|
|
|
+ // 处理远程URL文件
|
|
|
|
+ if (filter_var($filePathu, FILTER_VALIDATE_URL)) {
|
|
|
|
+ error_log("DownloadFile: Processing remote file $key: $filePathu");
|
|
|
|
+ $this->addRemoteFileToZip($zip, $filePathu, $innerFileName);
|
|
|
|
+ $addedCount++;
|
|
|
|
+ } else {
|
|
|
|
+ // 处理本地文件
|
|
|
|
+ error_log("DownloadFile: Processing local file $key: $filePathu");
|
|
|
|
+ if (!file_exists($filePathu)) {
|
|
|
|
+ // 尝试添加public前缀
|
|
|
|
+ $fullPath = 'public/' . $filePathu;
|
|
|
|
+ if (!file_exists($fullPath)) {
|
|
|
|
+ var_dump("local路径:",BASE_PATH);
|
|
|
|
+ // 尝试使用绝对路径
|
|
|
|
+ $fullPath = BASE_PATH . '/public/' . $filePathu;
|
|
|
|
+ if (!file_exists($fullPath)) {
|
|
|
|
+ error_log("DownloadFile: File not found: $filePathu");
|
|
|
|
+ $failedCount++;
|
|
|
|
+ continue; // 跳过不存在的文件
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ $filePathu = $fullPath;
|
|
}
|
|
}
|
|
- }
|
|
|
|
-
|
|
|
|
- // 远程:使用预下载的临时文件
|
|
|
|
- if (array_key_exists($index, $tempFiles)) {
|
|
|
|
- $tempPath = $tempFiles[$index];
|
|
|
|
- if (is_file($tempPath) && is_readable($tempPath)) {
|
|
|
|
- if ($zip->addFile($tempPath, $zipInnerName)) {
|
|
|
|
- $addedAny = true;
|
|
|
|
|
|
+
|
|
|
|
+ // 确保文件存在且可读
|
|
|
|
+ if (is_file($filePathu) && is_readable($filePathu)) {
|
|
|
|
+ if ($zip->addFile($filePathu, $innerFileName)) {
|
|
|
|
+ $addedCount++;
|
|
|
|
+ error_log("DownloadFile: Successfully added local file: $innerFileName");
|
|
|
|
+ } else {
|
|
|
|
+ $failedCount++;
|
|
|
|
+ error_log("DownloadFile: Failed to add local file: $innerFileName");
|
|
}
|
|
}
|
|
|
|
+ } else {
|
|
|
|
+ $failedCount++;
|
|
|
|
+ error_log("DownloadFile: File not readable: $filePathu");
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ error_log("DownloadFile: Summary - Added: $addedCount, Failed: $failedCount");
|
|
|
|
+
|
|
|
|
+ // 关闭zip文件
|
|
|
|
+ $closeResult = $zip->close();
|
|
|
|
+ error_log("DownloadFile: ZIP close result: " . ($closeResult ? 'success' : 'failed'));
|
|
|
|
+
|
|
|
|
+ if (!$closeResult) {
|
|
|
|
+ error_log("DownloadFile: Failed to close ZIP file: $zipFileName");
|
|
|
|
+ return Result::error('ZIP文件关闭失败');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 检查ZIP文件是否创建成功
|
|
|
|
+ if (!file_exists($zipFileName)) {
|
|
|
|
+ error_log("DownloadFile: ZIP file does not exist after close: $zipFileName");
|
|
|
|
+ return Result::error('ZIP文件创建失败');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $zipFileSize = filesize($zipFileName);
|
|
|
|
+ if ($zipFileSize === false || $zipFileSize === 0) {
|
|
|
|
+ error_log("DownloadFile: ZIP file is empty or cannot get size: $zipFileName");
|
|
|
|
+ return Result::error('ZIP文件为空');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ error_log("DownloadFile: ZIP file created successfully: $zipFileName (size: $zipFileSize bytes)");
|
|
|
|
+
|
|
|
|
+ // 将zip文件提供给用户进行下载
|
|
|
|
+ $fileUrlName = explode("public", $zipFileName);
|
|
|
|
+ if (count($fileUrlName) < 2) {
|
|
|
|
+ return Result::error('无法生成下载链接');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $downloadUrl = env('HOST') . $fileUrlName[1];
|
|
|
|
+ error_log("DownloadFile: Download URL: $downloadUrl");
|
|
|
|
+
|
|
|
|
+ return Result::success(['fileUrl' => $downloadUrl]);
|
|
|
|
+ } else {
|
|
|
|
+ return Result::error('无法创建zip文件');
|
|
|
|
+ }"DownloadFile: Full directory path: $allDir");
|
|
|
|
+
|
|
|
|
+ if (!is_dir($allDir)) {
|
|
|
|
+ if (!mkdir($allDir, 0755, true)) {
|
|
|
|
+ error_log("DownloadFile: Failed to create directory: $allDir");
|
|
|
|
+ return Result::error('创建文件夹失败');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // 生成绝对路径的ZIP文件名
|
|
|
|
+ $zipFileName = $allDir . DIRECTORY_SEPARATOR . $requireData['fileName'] . time() . mt_rand(1, 1000000) . '.zip';
|
|
|
|
+// $zipFileName = 'public/zip/files.zip';
|
|
|
|
+
|
|
|
|
+ error_log("DownloadFile: ZIP file path: $zipFileName");
|
|
|
|
+ error_log("DownloadFile: Directory exists: " . (is_dir($allDir) ? 'yes' : 'no'));
|
|
|
|
+ error_log("DownloadFile: Directory writable: " . (is_writable($allDir) ? 'yes' : 'no'));
|
|
|
|
+
|
|
|
|
+ // 添加OVERWRITE标志确保文件可以被创建
|
|
|
|
+ if ($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
|
|
|
|
+ // 将要下载的文件逐个添加到zip文件中
|
|
|
|
+ /** @var array<string> $files */
|
|
|
|
+ $files = [];
|
|
|
|
+ if (is_array($requireData['files'])) {
|
|
|
|
+ $files = $requireData['files'];
|
|
|
|
+ } elseif (is_string($requireData['files'])) {
|
|
|
|
+ $files = [$requireData['files']];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 添加进度日志
|
|
|
|
+ error_log("DownloadFile: Processing " . count($files) . " files");
|
|
|
|
+
|
|
|
|
+ $addedCount = 0;
|
|
|
|
+ $failedCount = 0;
|
|
|
|
+
|
|
|
|
+ foreach ($files as $key => $filePathu) {
|
|
|
|
+ if (!is_string($filePathu)) {
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 本地文件处理
|
|
|
|
- $localPath = $originalPath;
|
|
|
|
- if (!file_exists($localPath)) {
|
|
|
|
- // 尝试添加 public 前缀
|
|
|
|
- $fullPath = 'public/' . ltrim($localPath, '/');
|
|
|
|
- if (!file_exists($fullPath)) {
|
|
|
|
- // 尝试使用绝对路径
|
|
|
|
- $fullPath = BASE_PATH . '/public/' . ltrim($localPath, '/');
|
|
|
|
|
|
+
|
|
|
|
+ // 生成文件名:key + 1
|
|
|
|
+ $innerFileName = $requireData['names'][$key] ?? 'file_' . ($key + 1); //($key + 1) . $this->getFileExtension($filePathu);
|
|
|
|
+
|
|
|
|
+ // 处理远程URL文件
|
|
|
|
+ if (filter_var($filePathu, FILTER_VALIDATE_URL)) {
|
|
|
|
+ error_log("DownloadFile: Processing remote file $key: $filePathu");
|
|
|
|
+ $this->addRemoteFileToZip($zip, $filePathu, $innerFileName);
|
|
|
|
+ $addedCount++;
|
|
|
|
+ } else {
|
|
|
|
+ // 处理本地文件
|
|
|
|
+ error_log("DownloadFile: Processing local file $key: $filePathu");
|
|
|
|
+ if (!file_exists($filePathu)) {
|
|
|
|
+ // 尝试添加public前缀
|
|
|
|
+ $fullPath = 'public/' . $filePathu;
|
|
if (!file_exists($fullPath)) {
|
|
if (!file_exists($fullPath)) {
|
|
- continue; // 跳过不存在的文件
|
|
|
|
|
|
+ var_dump("local路径:",BASE_PATH);
|
|
|
|
+ // 尝试使用绝对路径
|
|
|
|
+ $fullPath = BASE_PATH . '/public/' . $filePathu;
|
|
|
|
+ if (!file_exists($fullPath)) {
|
|
|
|
+ error_log("DownloadFile: File not found: $filePathu");
|
|
|
|
+ $failedCount++;
|
|
|
|
+ continue; // 跳过不存在的文件
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ $filePathu = $fullPath;
|
|
}
|
|
}
|
|
- $localPath = $fullPath;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (is_file($localPath) && is_readable($localPath)) {
|
|
|
|
- if ($zip->addFile($localPath, $zipInnerName)) {
|
|
|
|
- $addedAny = true;
|
|
|
|
|
|
+
|
|
|
|
+ // 确保文件存在且可读
|
|
|
|
+ if (is_file($filePathu) && is_readable($filePathu)) {
|
|
|
|
+ if ($zip->addFile($filePathu, $innerFileName)) {
|
|
|
|
+ $addedCount++;
|
|
|
|
+ error_log("DownloadFile: Successfully added local file: $innerFileName");
|
|
|
|
+ } else {
|
|
|
|
+ $failedCount++;
|
|
|
|
+ error_log("DownloadFile: Failed to add local file: $innerFileName");
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ $failedCount++;
|
|
|
|
+ error_log("DownloadFile: File not readable: $filePathu");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- } finally {
|
|
|
|
- // 关闭 zip
|
|
|
|
- if ($zipOpened) {
|
|
|
|
- $zip->close();
|
|
|
|
- } elseif (file_exists($zipFileName) && filesize($zipFileName) === 0) {
|
|
|
|
- // 若未成功打开或写入,确保不会遗留空文件
|
|
|
|
- @unlink($zipFileName);
|
|
|
|
|
|
+
|
|
|
|
+ error_log("DownloadFile: Summary - Added: $addedCount, Failed: $failedCount");
|
|
|
|
+
|
|
|
|
+ // 关闭zip文件
|
|
|
|
+ $closeResult = $zip->close();
|
|
|
|
+ error_log("DownloadFile: ZIP close result: " . ($closeResult ? 'success' : 'failed'));
|
|
|
|
+
|
|
|
|
+ if (!$closeResult) {
|
|
|
|
+ error_log("DownloadFile: Failed to close ZIP file: $zipFileName");
|
|
|
|
+ return Result::error('ZIP文件关闭失败');
|
|
}
|
|
}
|
|
- // 清理临时文件
|
|
|
|
- foreach ($tempFiles as $temp) {
|
|
|
|
- @unlink($temp);
|
|
|
|
|
|
+
|
|
|
|
+ // 检查ZIP文件是否创建成功
|
|
|
|
+ if (!file_exists($zipFileName)) {
|
|
|
|
+ error_log("DownloadFile: ZIP file does not exist after close: $zipFileName");
|
|
|
|
+ return Result::error('ZIP文件创建失败');
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ $zipFileSize = filesize($zipFileName);
|
|
|
|
+ if ($zipFileSize === false || $zipFileSize === 0) {
|
|
|
|
+ error_log("DownloadFile: ZIP file is empty or cannot get size: $zipFileName");
|
|
|
|
+ return Result::error('ZIP文件为空');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ error_log("DownloadFile: ZIP file created successfully: $zipFileName (size: $zipFileSize bytes)");
|
|
|
|
+
|
|
|
|
+ // 将zip文件提供给用户进行下载
|
|
|
|
+ $fileUrlName = explode("public", $zipFileName);
|
|
|
|
+ if (count($fileUrlName) < 2) {
|
|
|
|
+ return Result::error('无法生成下载链接');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $downloadUrl = env('HOST') . $fileUrlName[1];
|
|
|
|
+ error_log("DownloadFile: Download URL: $downloadUrl");
|
|
|
|
+
|
|
|
|
+ return Result::success(['fileUrl' => $downloadUrl]);
|
|
|
|
+ } else {
|
|
|
|
+ return Result::error('无法创建zip文件');
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (!$addedAny || !file_exists($zipFileName)) {
|
|
|
|
- return Result::error('打包失败或没有可用的文件');
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 返回可下载地址
|
|
|
|
- $fileUrlName = explode('public', $zipFileName);
|
|
|
|
- return Result::success(['fileUrl' => env('HOST') . $fileUrlName[1]]);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -525,9 +620,15 @@ class PublicController extends AbstractController
|
|
*/
|
|
*/
|
|
private function addRemoteFileToZip($zip, $url, $fileName = null): void
|
|
private function addRemoteFileToZip($zip, $url, $fileName = null): void
|
|
{
|
|
{
|
|
|
|
+ var_dump("$$$$$$$$$$$$$$$$$$$$");
|
|
|
|
+ $tempFile = null;
|
|
try {
|
|
try {
|
|
// 创建临时文件
|
|
// 创建临时文件
|
|
$tempFile = tempnam(sys_get_temp_dir(), 'remote_file_');
|
|
$tempFile = tempnam(sys_get_temp_dir(), 'remote_file_');
|
|
|
|
+ if ($tempFile === false) {
|
|
|
|
+ error_log("DownloadFile: Failed to create temp file for URL: $url");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
// 设置更长的超时时间和更好的下载参数
|
|
// 设置更长的超时时间和更好的下载参数
|
|
$context = stream_context_create([
|
|
$context = stream_context_create([
|
|
@@ -558,26 +659,26 @@ class PublicController extends AbstractController
|
|
}
|
|
}
|
|
|
|
|
|
if ($fileContent === false) {
|
|
if ($fileContent === false) {
|
|
- error_log("Failed to download remote file: " . $url);
|
|
|
|
|
|
+ error_log("DownloadFile: Failed to download remote file: " . $url);
|
|
return; // 下载失败,跳过
|
|
return; // 下载失败,跳过
|
|
}
|
|
}
|
|
|
|
|
|
// 验证文件内容
|
|
// 验证文件内容
|
|
if (empty($fileContent) || strlen($fileContent) < 100) {
|
|
if (empty($fileContent) || strlen($fileContent) < 100) {
|
|
- error_log("Downloaded file is too small or empty: " . $url);
|
|
|
|
|
|
+ error_log("DownloadFile: Downloaded file is too small or empty: " . $url . " (size: " . strlen($fileContent) . ")");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// 写入临时文件并验证
|
|
// 写入临时文件并验证
|
|
$bytesWritten = file_put_contents($tempFile, $fileContent);
|
|
$bytesWritten = file_put_contents($tempFile, $fileContent);
|
|
if ($bytesWritten === false || $bytesWritten !== strlen($fileContent)) {
|
|
if ($bytesWritten === false || $bytesWritten !== strlen($fileContent)) {
|
|
- error_log("Failed to write file content: " . $url);
|
|
|
|
|
|
+ error_log("DownloadFile: Failed to write file content: " . $url . " (written: $bytesWritten, expected: " . strlen($fileContent) . ")");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// 验证文件是否可读
|
|
// 验证文件是否可读
|
|
if (!is_readable($tempFile) || filesize($tempFile) < 100) {
|
|
if (!is_readable($tempFile) || filesize($tempFile) < 100) {
|
|
- error_log("Temporary file is not readable or too small: " . $tempFile);
|
|
|
|
|
|
+ error_log("DownloadFile: Temporary file is not readable or too small: " . $tempFile . " (size: " . filesize($tempFile) . ")");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -591,13 +692,20 @@ class PublicController extends AbstractController
|
|
|
|
|
|
// 添加到zip并验证
|
|
// 添加到zip并验证
|
|
if (!$zip->addFile($tempFile, $fileName)) {
|
|
if (!$zip->addFile($tempFile, $fileName)) {
|
|
- error_log("Failed to add file to zip: " . $fileName);
|
|
|
|
|
|
+ error_log("DownloadFile: Failed to add file to zip: " . $fileName);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ error_log("DownloadFile: Successfully added remote file to ZIP: $fileName");
|
|
|
|
+
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
// 记录错误但继续处理其他文件
|
|
// 记录错误但继续处理其他文件
|
|
- error_log("Failed to download remote file: " . $url . " - " . $e->getMessage());
|
|
|
|
|
|
+ error_log("DownloadFile: Exception while downloading remote file: " . $url . " - " . $e->getMessage());
|
|
|
|
+ } finally {
|
|
|
|
+ // 清理临时文件
|
|
|
|
+ if ($tempFile !== null && file_exists($tempFile)) {
|
|
|
|
+ @unlink($tempFile);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -634,108 +742,30 @@ class PublicController extends AbstractController
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$contentLength = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
|
|
$contentLength = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
|
|
$actualLength = strlen($content);
|
|
$actualLength = strlen($content);
|
|
|
|
+ $error = curl_error($ch);
|
|
curl_close($ch);
|
|
curl_close($ch);
|
|
|
|
|
|
// 检查HTTP状态码和内容长度
|
|
// 检查HTTP状态码和内容长度
|
|
if ($content === false || $httpCode !== 200) {
|
|
if ($content === false || $httpCode !== 200) {
|
|
- error_log("cURL download failed for URL: $url, HTTP Code: $httpCode");
|
|
|
|
|
|
+ error_log("DownloadFile: cURL download failed for URL: $url, HTTP Code: $httpCode, Error: $error");
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// 检查内容长度是否匹配
|
|
// 检查内容长度是否匹配
|
|
if ($contentLength > 0 && $actualLength !== $contentLength) {
|
|
if ($contentLength > 0 && $actualLength !== $contentLength) {
|
|
- error_log("Content length mismatch for URL: $url, Expected: $contentLength, Actual: $actualLength");
|
|
|
|
|
|
+ error_log("DownloadFile: Content length mismatch for URL: $url, Expected: $contentLength, Actual: $actualLength");
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// 检查文件是否为空或太小
|
|
// 检查文件是否为空或太小
|
|
if (empty($content) || $actualLength < 100) {
|
|
if (empty($content) || $actualLength < 100) {
|
|
- error_log("File too small or empty for URL: $url, Size: $actualLength");
|
|
|
|
|
|
+ error_log("DownloadFile: File too small or empty for URL: $url, Size: $actualLength");
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
return $content;
|
|
return $content;
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * 下载远程文件到临时文件(流式写入,节省内存),成功返回临时文件路径,失败返回 null
|
|
|
|
- */
|
|
|
|
- private function downloadRemoteToTemp(string $url, int $timeoutSeconds = 120): ?string
|
|
|
|
- {
|
|
|
|
- // 优先使用 cURL 流式写入
|
|
|
|
- if (function_exists('curl_init')) {
|
|
|
|
- $tempFile = tempnam(sys_get_temp_dir(), 'remote_file_');
|
|
|
|
- if ($tempFile === false) {
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- $fp = @fopen($tempFile, 'wb');
|
|
|
|
- if ($fp === false) {
|
|
|
|
- @unlink($tempFile);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- $ch = curl_init();
|
|
|
|
- curl_setopt_array($ch, [
|
|
|
|
- CURLOPT_URL => $url,
|
|
|
|
- CURLOPT_FILE => $fp, // 直接写文件
|
|
|
|
- CURLOPT_FOLLOWLOCATION => true,
|
|
|
|
- CURLOPT_MAXREDIRS => 5,
|
|
|
|
- CURLOPT_CONNECTTIMEOUT => 30,
|
|
|
|
- CURLOPT_TIMEOUT => $timeoutSeconds,
|
|
|
|
- CURLOPT_SSL_VERIFYPEER => false,
|
|
|
|
- CURLOPT_SSL_VERIFYHOST => false,
|
|
|
|
- CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
|
|
- CURLOPT_HTTPHEADER => [
|
|
|
|
- 'Accept: */*',
|
|
|
|
- 'Connection: keep-alive',
|
|
|
|
- 'Cache-Control: no-cache',
|
|
|
|
- ],
|
|
|
|
- ]);
|
|
|
|
- $ok = curl_exec($ch);
|
|
|
|
- $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
- curl_close($ch);
|
|
|
|
- fclose($fp);
|
|
|
|
-
|
|
|
|
- if ($ok !== false && $httpCode === 200 && is_file($tempFile) && filesize($tempFile) >= 100) {
|
|
|
|
- return $tempFile;
|
|
|
|
- }
|
|
|
|
- @unlink($tempFile);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 退化为 stream_context + file_get_contents 方案
|
|
|
|
- $context = stream_context_create([
|
|
|
|
- 'http' => [
|
|
|
|
- 'timeout' => $timeoutSeconds,
|
|
|
|
- 'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
|
|
- 'follow_location' => true,
|
|
|
|
- 'max_redirects' => 5,
|
|
|
|
- 'protocol_version' => 1.1,
|
|
|
|
- 'header' => [
|
|
|
|
- 'Accept: */*',
|
|
|
|
- 'Connection: keep-alive'
|
|
|
|
- ],
|
|
|
|
- ],
|
|
|
|
- 'ssl' => [
|
|
|
|
- 'verify_peer' => false,
|
|
|
|
- 'verify_peer_name' => false,
|
|
|
|
- 'allow_self_signed' => true,
|
|
|
|
- ],
|
|
|
|
- ]);
|
|
|
|
- $content = @file_get_contents($url, false, $context);
|
|
|
|
- if ($content === false || strlen((string) $content) < 100) {
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- $tempFile = tempnam(sys_get_temp_dir(), 'remote_file_');
|
|
|
|
- if ($tempFile === false) {
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- $bytes = @file_put_contents($tempFile, $content);
|
|
|
|
- if ($bytes === false || $bytes < 100) {
|
|
|
|
- @unlink($tempFile);
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
- return $tempFile;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* 获取文件扩展名
|
|
* 获取文件扩展名
|
|
*/
|
|
*/
|