跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

PHP 远程服务器文本文件编辑:正确使用 FTP 上传与覆盖文件的完整教程

本文详解如何在 PHP 中通过 FTP 安全、可靠地编辑远程服务器上的文本文件,重点解决 ftp_fput() 失败、file_exists() 误判路径、文件零字节等常见问题,并提供可直接复用的健壮实现方案。 本文详解如何在 php 中通过 ftp 安全、可靠地编辑远程服务器上的文本文件,重点解决 `ftp_fput()` 失败、`file_exists()` 误判路径、文件零字节等常见问题,并提供可直接复用的健壮实现方案。 在 Web 应用多站点协同场景中(如统一域名白名单管理),常需从不同子站动态更新部署在同一主站的共享配置文件(如 email_domain_whitelist.txt)。然而,直接使用 PHP 的 FTP 扩展操作远程文件时,开发者极易陷入以下典型误区: ❌ 错误地将 HTTP URL(如 https://example.com/...)传入 file_exists() 或 ftp_delete() —— 这些函数 不支持远程 URL ,仅作用于本地文件系统; ❌ 混淆 ftp_fput() 与 ftp_put():ftp_fput() 需传入已打开的 resource 句柄 (如 fopen() 返回值),但若该句柄指向未正确写入或未关闭的临时文件,极易导致上传空文件(0 字节); ❌ 手动删除再上传——既增加失败风险,又非必要:ftp_put() 在目标路径存在同名文件时 自动覆盖 ,无需显式调用 ftp_delete()。 ✅ 正确做法是: 本地生成临时文件 → 写入内容 → 关闭句柄 → 使用 ftp_put() 直接上传覆盖 。以下是优化后的核心流程与可生产环境使用的完整代码示例: ✅ 推荐实现:安全、简洁、幂等
function eri_update_global_whitelist($string, $add_or_remove = 'add') { // 1. 域名清洗与校验(保持原逻辑) $domains = array_filter( array_map('trim', explode(',', strtolower(str_replace(' ', '', $string)))), function($d) { return strpos($d, '.') !== false; } ); $invalid = array_diff(explode(',', strtolower(str_replace(' ', '', $string))), $domains); if (empty($domains)) { echo '
No valid domains to process.'; return; } $filename = 'email_domain_whitelist.txt'; $remote_path = '/eri-webtools-plugin/data/' . $filename; // 注意:这是 FTP 服务器上的相对路径(从根目录起) // 2. 读取远程现有内容(通过 HTTP,仅用于读取) $remote_url = 'https://example.com/eri-webtools-plugin/data/' . $filename; $current_content = @file_get_contents($remote_url) ?: ''; $existing_domains = array_filter( array_map('trim', explode(',', strtolower(str_replace(' ', '', $current_content)))), function($d) { return strpos($d, '.') !== false; } ); // 3. 合并/过滤逻辑 $new_domains = $add_or_remove === 'add' ? array_values(array_unique(array_merge($existing_domains, $domains))) : array_values(array_diff($existing_domains, $domains)); // 4. 生成本地临时文件(关键!) $local_temp_file = $filename; // 简单起见,使用同名;生产环境建议用 tempnam() $fp = fopen($local_temp_file, 'w'); if (!$fp) { echo '
Failed to create local temp file.'; return; } $content_to_write = implode(', ', $new_domains); fwrite($fp, $content_to_write); fclose($fp); // ⚠️ 必须关闭!否则 ftp_put 可能读取到空/缓存内容 // 5. FTP 连接与上传(自动覆盖) require_once $_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/eri-webtools-plugin/ftp_config.php'; $conn = ftp_connect($ftp_server); if (!$conn) { echo '
FTP connection failed.'; return; } $login = ftp_login($conn, $ftp_username, $ftp_userpass); if (!$login) { echo '
FTP login failed.'; ftp_close($conn); return; } ftp_pasv($conn, true); // 启用被动模式(推荐) // ✅ 核心修复:使用 ftp_put() + 本地文件路径(字符串),非 resource if (ftp_put($conn, $remote_path, $local_temp_file, FTP_ASCII)) { echo '
✅ Successfully updated remote whitelist: ' . count($new_domains) . ' domains.'; } else { echo '
❌ FTP upload failed. Check permissions and path.'; } ftp_close($conn); // 6. 清理本地临时文件(可选但推荐) @unlink($local_temp_file); }
? 关键注意事项总结 路径语义区分清晰 : file_get_contents('https://...') ✅ 仅用于 读取 远程 HTTP 资源; ftp_put($conn, '/remote/path.txt', 'local_file.txt', ...) ✅ remote_path 是 FTP 服务器上的 绝对路径(从根起) ,local_file.txt 是本地磁盘上的 完整文件路径(字符串) ; file_exists('https://...') ❌ 永远返回 false ,切勿使用。 资源管理铁律 : PHP 8.5.5 PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。 下载 立即学习 “ PHP免费学习笔记(深入) ”; fopen() 后必须 fclose(),否则 ftp_put() 可能因文件句柄未刷新而上传空内容; 临时文件建议使用 tempnam(sys_get_temp_dir(), 'whitelist_') 生成唯一路径,避免并发冲突。 错误处理增强建议 (生产环境必备): 使用 ftp_ssl_connect() 替代 ftp_connect()(如服务器支持); 对 ftp_put() 失败添加重试机制与日志记录; 白名单更新前先备份远程文件(ftp_rename($remote_path, $remote_path . '.bak'))。 通过以上重构,你将获得一个稳定、可维护、符合最佳实践的远程配置文件编辑方案——不再依赖脆弱的 ftp_fput(),也不再为“文件找不到”或“上传空白”而深夜调试。

相关文章