
本文详解如何通过 php 批量下载 `.adm` 文件、将其重命名为 `.txt`,再将所有文件内容追加写入一个统一的输出文件,重点解决 `fputs()` 参数类型错误及资源操作误区。
在自动化日志处理或配置文件聚合场景中,常需从远程 URL 批量获取文件(如 .ADM 格式策略文件),统一转换为 .txt 并合并为单一结果文件。但初学者易在文件流操作中混淆「文件资源」与「文件内容」,导致类似 Warning: fputs() expects parameter 2 to be string, resource given 的典型错误——这正源于误将 fopen() 返回的资源句柄直接传给 fputs(),而该函数第二参数必须是字符串内容。
✅ 正确做法:内容导向,而非资源导向
fputs()(等价于 fwrite())用于向文件资源写入字符串数据,而非另一个文件资源。因此,关键步骤是:先读取源文件内容,再写入目标文件。以下是优化后的完整实现:
// 确保输出目录存在
$outputDir = 'tmp/';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0755, true);
}
$outputFile = $outputDir . 'merged_output.txt';
// 使用 'ab' 模式(追加二进制)确保多次循环内容不被覆盖
$handleOut = fopen($outputFile, 'ab');
if (!$handleOut) {
throw new RuntimeException("无法打开输出文件: {$outputFile}");
}
foreach ($items as $logfile) {
$downloadUrl = $logfile['Download'];
$originalName = $logfile['name'];
// 1. 下载并保存为 .txt 文件
$newName = str_replace('.ADM', '.txt', $originalName); // 更精准:替换扩展名而非子串
$localPath = $newName;
$content = file_get_contents($downloadUrl);
if ($content === false) {
error_log("警告:无法下载 {$downloadUrl}");
continue;
}
// 写入本地 .txt 文件(可选:用于调试或备份)
file_put_contents($localPath, $content);
// 2. 将内容追加到合并文件(无需额外 fopen/fread)
fwrite($handleOut, $content);
fwrite($handleOut, "\n" . str_repeat('-', 60) . "\n"); // 可选:添加分隔线便于阅读
}
fclose($handleOut);
echo "✅ 合并完成!总文件数:" . count($items) . ",输出路径:{$outputFile}\n";⚠️ 关键注意事项
- 避免 fopen + fputs 嵌套陷阱:原代码中 $file = fopen(...) 得到的是资源,直接传给 fputs($output, $file) 必然报错。务必使用 file_get_contents() 或 fread() 提取字符串内容。
-
扩展名替换要严谨:str_replace('ADM','txt',$str) 可能误改文件名中的 ADM 子串(如 myADMreport.ADM → mytxtreport.txt)。推荐用 pathinfo() 或正则确保仅替换后缀:
$newName = preg_replace('/\.ADM$/i', '.txt', $originalName); - I/O 效率优化:无需先写入磁盘再读取——file_get_contents($downloadUrl) 获取内容后,可直接 fwrite() 到合并句柄,减少磁盘 I/O。
- 错误处理不可省略:网络请求可能失败,应检查 file_get_contents() 返回值;fopen() 也需判空,防止后续操作崩溃。
- 编码与换行一致性:若源 .ADM 文件含 BOM 或混合编码,合并后可能出现乱码。建议统一转为 UTF-8(可用 mb_convert_encoding() 处理)。
✅ 替代方案:更简洁的流式合并(推荐)
若无需保留中间 .txt 文件,可一步到位:
$handleOut = fopen('tmp/merged.txt', 'wb');
foreach ($items as $item) {
$content = @file_get_contents($item['Download']); // @ 抑制警告,实际项目中建议用 try/catch
if ($content !== false) {
fwrite($handleOut, $content . "\n\n");
}
}
fclose($handleOut);掌握「内容即数据、资源即通道」这一核心理念,即可安全高效地完成批量文件下载、格式转换与内容聚合任务。










