PHP 文件下载、扩展名替换与内容合并的正确实践

5次阅读

PHP 文件下载、扩展名替换与内容合并的正确实践

本文详解如何在 php 中安全下载远程文件、批量修改扩展名(如 .adm → .txt),并高效合并所有文件内容到单一输出文件,重点纠正 `fputs()` 误传资源句柄的常见错误。

在实际运维或数据采集场景中,常需批量下载特定格式的远程文件(例如 .ADM 配置日志),统一重命名为标准文本格式(如 .txt),再将全部内容追加写入一个汇总文件。但初学者易混淆 php 的文件 I/O 操作层级——尤其误将文件资源(Resource)直接传给期望字符串内容的函数(如 fputs()),导致“Warning: fputs() expects parameter 2 to be String, resource given”报错。

根本原因在于:fputs($stream, $string) 的第二个参数必须是字符串内容,而非 fopen() 返回的文件资源。原代码中 $file = fopen(…) 创建的是资源句柄,后续 fputs($output, $file) 将该句柄当作内容传递,必然失败。

✅ 正确做法有两类,推荐优先使用简洁安全的函数式方案:

方案一:全程使用 file_get_contents() / file_put_contents()(推荐)

$outputFile = 'tmp/test.txt'; // 清空或创建目标文件(确保从头写入) file_put_contents($outputFile, '', LOCK_EX);  foreach ($items as $logfile) {     $url = $logfile['Download'];     $originalName = $logfile['name'];     $newName = str_replace('.ADM', '.txt', $originalName); // 注意:建议用 .ADM 而非 ADM,避免误替换路径中的子串      // 下载并保存为新文件     $content = file_get_contents($url);     if ($content === false) {         error_log("Failed to download: {$url}");         continue;     }     file_put_contents($newName, $content, LOCK_EX);      // 追加内容到汇总文件(自动换行分隔,提升可读性)     file_put_contents($outputFile, $content . "n" . str_repeat('-', 50) . "n", FILE_APPEND | LOCK_EX); }

方案二:使用流式操作(适用于超大文件,避免内存溢出)

$outputFile = 'tmp/test.txt'; $fpOut = fopen($outputFile, 'wb'); if (!$fpOut) {     throw new RuntimeException("Cannot open output file: {$outputFile}"); }  foreach ($items as $logfile) {     $url = $logfile['Download'];     $newName = str_replace('.ADM', '.txt', $logfile['name']);      // 下载并保存     $content = file_get_contents($url);     if ($content === false) continue;     file_put_contents($newName, $content);      // 流式读取并写入(无需全量加载到内存)     $fpIn = fopen($newName, 'rb');     if ($fpIn) {         while (!feof($fpIn)) {             $chunk = fread($fpIn, 8192); // 每次读取 8KB             fwrite($fpOut, $chunk);         }         fclose($fpIn);         fwrite($fpOut, "n" . str_repeat('-', 50) . "n"); // 分隔符     } } fclose($fpOut);

⚠️ 关键注意事项:

  • 扩展名替换要精准:使用 str_replace(‘.ADM’, ‘.txt’, $name) 而非 str_replace(‘ADM’,’txt’,$name),防止文件名含 ADM 字符串(如 myADMreport.ADM)被错误截断。
  • 错误处理不可省略:file_get_contents() 失败时返回 false,需显式检查,否则后续操作会出错。
  • 文件锁机制:多进程并发时,使用 LOCK_EX 参数避免写入冲突。
  • 路径安全性:确保 $logfile[‘name’] 不含恶意路径(如 ../etc/passwd),生产环境应校验或白名单过滤文件名。
  • 内存考量:若单个文件超百 MB,优先选用方案二的流式处理,避免 file_get_contents() 导致内存耗尽。

通过以上重构,您不仅能彻底规避 fputs() 参数类型错误,还能获得健壮、可维护、符合生产要求的文件批量处理脚本。

立即学习PHP免费学习笔记(深入)”;

text=ZqhQzanResources