PHP通过fopen写入远程FTP服务器仅生成空文件问题求助
问题描述
将表单数据保存到远程FTP服务器时遇到异常:
- 本地路径保存完全正常,代码如下:
$filename = "/home/fvaeytm/test/uploads/user_dirs/$user_login".date("Ymdhis").".txt"; - 修改为FTP路径后:
远程FTP服务器会创建文件名正确的空文件,且文件生成后约30秒内无法打开。$filename = "ftp://pi:pi@ftp.domain.com/home/pi/test/" . $user_login . date("Ymdhis") . ".txt"; - 核心代码逻辑:
$filename = "/home/fvaeytm/test/uploads/user_dirs/$user_login".date("Ymdhis").".txt"; $myfile = fopen($filename, "w"); fwrite($myfile, $Name."\n"); fwrite($myfile, $Number."\n"); fclose($myfile); echo "<div id='success_page'>"; echo "<p>OK</p>"; echo "</div>"; } - 已确认信息:
- PHP的
allow_url_fopen已设置为On - 修改FTP服务器文件权限无效果
- 在OVH的FTP服务器测试正常,但自家两台FTP(OpenWrt和Debian树莓派)均出现相同问题
- 通过FTP客户端使用外网IP连接自家服务器,可正常创建并写入文件,域名托管在Cloudflare且已关闭SSL
- PHP的
解决方案
针对家用FTP服务器出现的空文件问题,可按以下步骤排查修复:
1. 强制启用被动模式(Passive Mode)
家用FTP服务器通常处于NAT环境下,PHP默认的fopen使用FTP主动模式可能导致数据连接失败,无法写入内容。可以通过stream_context_create指定被动模式:
// 创建FTP上下文,启用被动模式 $context = stream_context_create([ 'ftp' => [ 'passive' => true, ] ]); // 打开文件时传入上下文 $myfile = fopen($filename, "w", false, $context); // 后续写入、关闭逻辑保持不变 fwrite($myfile, $Name."\n"); fwrite($myfile, $Number."\n"); fclose($myfile);
2. 检查FTP服务器的被动模式端口范围映射
如果启用被动模式后仍有问题,需确认家用路由器是否正确映射了FTP服务器的被动模式端口范围(通常是一段端口,比如21000-22000),并在FTP服务器配置中指定该端口范围:
- 对于vsftpd(Debian树莓派常用):在
/etc/vsftpd.conf中添加
然后重启vsftpd服务pasv_min_port=21000 pasv_max_port=22000 - 对于OpenWrt上的FTP服务(如pure-ftpd):在配置中指定被动端口范围,同时在路由器防火墙中开放对应端口
3. 替换为FTP扩展函数(更可靠的方式)
PHP的allow_url_fopen通过FTP协议写入的兼容性有限,改用专门的FTP扩展函数(ftp_connect、ftp_login、ftp_put等)能获得更好的兼容性和错误排查能力:
// FTP服务器信息 $ftp_host = 'ftp.domain.com'; $ftp_user = 'pi'; $ftp_pass = 'pi'; $remote_dir = '/home/pi/test/'; $local_temp_file = tempnam(sys_get_temp_dir(), 'form_data'); // 先将内容写入本地临时文件 $temp_handle = fopen($local_temp_file, 'w'); fwrite($temp_handle, $Name."\n"); fwrite($temp_handle, $Number."\n"); fclose($temp_handle); // 连接FTP服务器 $conn_id = ftp_connect($ftp_host); $login_result = ftp_login($conn_id, $ftp_user, $ftp_pass); // 启用被动模式 ftp_pasv($conn_id, true); // 上传临时文件到远程服务器 $remote_filename = $remote_dir . $user_login . date("Ymdhis") . ".txt"; if (ftp_put($conn_id, $remote_filename, $local_temp_file, FTP_BINARY)) { echo "<div id='success_page'><p>OK</p></div>"; } else { echo "<div id='error_page'><p>上传失败</p></div>"; } // 关闭连接并删除临时文件 ftp_close($conn_id); unlink($local_temp_file);
这种方式能更直观地排查连接、登录、上传各环节的错误,适合家用复杂网络环境。
4. 检查PHP的FTP扩展是否启用
如果使用上述FTP扩展函数时出现错误,需确认PHP的FTP扩展已启用:
- 在PHP配置文件(php.ini)中确保
extension=ftp未被注释 - 重启Web服务器后,通过
phpinfo()查看FTP扩展是否加载成功
5. 禁用Cloudflare的代理(临时测试)
虽然你已关闭SSL,但Cloudflare的DNS代理可能仍会影响FTP连接,可临时将域名切换为「DNS only」模式,直接解析到家用服务器外网IP,测试是否能正常写入。
内容的提问来源于stack exchange,提问作者l0gin
相关产品推荐
相关产品推荐

