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











