
本文详解如何在 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免费学习笔记(深入)”;