PHP使用ssh2_scp_recv获取SFTP文件失败及高效下载方案咨询
问题原因分析
- 未过滤scandir返回的特殊目录项:
scandir执行后会默认返回.(当前目录)、..(上级目录)两个特殊目录条目,代码直接对这两个条目调用ssh2_scp_recv,函数会因为目标路径是目录而非文件执行失败,且代码未做任何错误校验,因此看不到异常提示,看起来就像函数没有执行。 - 缺少路径与权限校验:一方面如果本地目录
/ftp_files没有给PHP进程开放写入权限,ssh2_scp_recv会直接写入失败;另一方面如果远程目录下存在子目录,ssh2_scp_recv不支持直接下载目录,同样会执行失败。 - 错误返回未处理:
ssh2_scp_recv执行失败时会返回布尔值false,代码没有对返回结果做判断,所有执行错误都被静默忽略。
修复后的ssh2_scp_recv实现(无需phpseclib)
function get_sftp_content(){ $url = 'someurl.com'; $port = 22; $username = "user"; $password = "password"; $connection = ssh2_connect($url, $port); if (!$connection) throw new Exception('Unable to connect to server.'); if (!ssh2_auth_password($connection, $username, $password)) throw new Exception('Authentication failed.'); $sftp = ssh2_sftp($connection); if (!$sftp) throw new Exception('Unable to initialize SFTP resource.'); $localDir = __DIR__ . '/ftp_files'; // 建议用绝对路径,避免相对路径出错 // 先判断本地目录是否存在,不存在则创建 if (!is_dir($localDir)) { mkdir($localDir, 0755, true); } $remoteDir = '/download'; $remoteBasePath = 'ssh2.sftp://' . intval($sftp) . $remoteDir; $files = scandir($remoteBasePath); if (!empty($files)) { foreach ($files as $file) { // 过滤特殊目录和子目录 if ($file == '.' || $file == '..' || is_dir($remoteBasePath . '/' . $file)) { continue; } $remoteFile = $remoteDir . '/' . $file; $localFile = $localDir . '/' . $file; $res = ssh2_scp_recv($connection, $remoteFile, $localFile); if (!$res) { echo "下载文件{$remoteFile}失败\n"; } } } // 断开连接 ssh2_disconnect($connection); } get_sftp_content();
更快的批量拉取替代方案
- 流拷贝方案:如果不想用SCP协议,可以用
ssh2_sftp打开远程文件流,调用原生stream_copy_to_stream函数做流拷贝,性能比手动循环fread高很多,代码示例:$remoteStream = fopen('ssh2.sftp://' . intval($sftp) . $remoteFile, 'rb'); $localStream = fopen($localFile, 'wb'); stream_copy_to_stream($remoteStream, $localStream); fclose($remoteStream); fclose($localStream); - 远程打包下载方案:如果你的SSH账号有权限执行 shell 命令,可以直接在远程将目标目录打包为单个压缩包,仅拉取压缩包到本地后解压,避免多次小文件传输的握手开销,是批量拉取大量小文件的最优方案,代码示例:
该方案下100MB总大小的文件,全程耗时通常在30秒以内。// 远程执行打包命令 $stream = ssh2_exec($connection, "cd /download && tar -zcf /tmp/download_package.tar.gz ."); stream_set_blocking($stream, true); fclose($stream); // 拉取打包后的单个文件 ssh2_scp_recv($connection, '/tmp/download_package.tar.gz', $localDir . '/download_package.tar.gz'); // 本地解压,可根据实际环境用exec调用tar命令或者PHP的ZipArchive类处理
内容的提问来源于stack exchange,提问作者Sergio Rodrigues
相关产品推荐
相关产品推荐

