PHP SFTP文件传输:解决ssh2.sftp://下载空文件问题

霞舞
发布: 2025-10-28 10:29:00
原创
541人浏览过

PHP SFTP文件传输:解决ssh2.sftp://下载空文件问题

本文旨在解决php使用`ssh2`扩展通过`ssh2.sftp://` uri从sftp服务器下载文件时,本地文件为空的问题。文章将详细解释导致此问题的原因,并提供两种有效的解决方案:一是利用`stream_copy_to_stream`函数进行流数据复制,二是推荐使用更简洁高效的`ssh2_scp_recv`函数(若服务器支持scp),确保文件内容能够正确传输到本地。

在使用PHP的ssh2扩展与SFTP服务器交互时,开发者可能会遇到一个常见问题:通过ssh2.sftp:// URI打开远程文件流并尝试保存到本地时,最终得到的本地文件却是空的。这通常是由于误解了fopen函数在此上下文中的行为所致。

理解问题:为何下载的文件为空?

原始代码片段如下:

$connection = ssh2_connect('{my server ip}', 22);
ssh2_auth_password($connection, '{user}', '{password}');
$sftp = ssh2_sftp($connection);
$file = "test.txt"; // 本地文件名
$remote_path = "/public_html/$file"; // 远程文件路径
$target_path = "ssh2.sftp://$sftp" . $remote_path; // 远程文件URI

$stream = fopen($target_path, 'r'); // 打开远程文件流
fopen($file, "w"); // 打开本地文件用于写入
fclose($stream); // 关闭远程文件流
登录后复制

这段代码的问题在于,它确实成功打开了远程文件作为可读流($stream),也成功创建了一个本地文件用于写入。然而,它从未执行将数据从远程流传输到本地文件的操作。fopen函数本身并不会进行数据复制,它只是建立了一个文件句柄或流。因此,在没有数据传输的情况下,本地文件自然会保持为空。

解决方案一:使用stream_copy_to_stream正确传输数据

要解决上述问题,我们需要明确地将数据从打开的远程流复制到打开的本地流。PHP提供了stream_copy_to_stream函数,专门用于此目的。

立即学习PHP免费学习笔记(深入)”;

stream_copy_to_stream(resource $source, resource $dest, ?int $maxlength = null, int $offset = 0): int|false

这个函数将从$source流中读取数据,并将其写入$dest流中,直到$source流到达末尾,或者复制了$maxlength字节的数据。

以下是使用stream_copy_to_stream的正确实现:

<?php

// 假设这些变量已正确配置
$server_ip = '{my server ip}';
$username = '{user}';
$password = '{password}';
$remote_file_name = "test.txt"; // 远程服务器上的文件名
$local_file_name = "downloaded_test.txt"; // 本地保存的文件名
$remote_base_path = "/public_html"; // 远程文件所在的目录

// 1. 建立SSH连接
$connection = ssh2_connect($server_ip, 22);
if (!$connection) {
    die("无法连接到服务器。\n");
}

// 2. 进行身份验证
if (!ssh2_auth_password($connection, $username, $password)) {
    die("身份验证失败。\n");
}

// 3. 获取SFTP子系统
$sftp = ssh2_sftp($connection);
if (!$sftp) {
    die("无法初始化SFTP子系统。\n");
}

// 4. 构建远程文件的完整SFTP URI
$remote_full_path = $remote_base_path . '/' . $remote_file_name;
$sftp_uri = "ssh2.sftp://$sftp" . $remote_full_path;

// 5. 打开远程文件流(只读)
$remote_stream = @fopen($sftp_uri, 'r');
if (!$remote_stream) {
    die("无法打开远程文件: " . $remote_full_path . "\n");
}

// 6. 打开本地文件流(写入模式)
$local_stream = @fopen($local_file_name, 'w');
if (!$local_stream) {
    fclose($remote_stream); // 确保关闭已打开的远程流
    die("无法创建本地文件: " . $local_file_name . "\n");
}

// 7. 将数据从远程流复制到本地流
$bytes_copied = stream_copy_to_stream($remote_stream, $local_stream);

// 8. 关闭所有流
fclose($remote_stream);
fclose($local_stream);

if ($bytes_copied !== false) {
    echo "文件 '" . $remote_file_name . "' 已成功下载到本地 '" . $local_file_name . "',共复制了 " . $bytes_copied . " 字节。\n";
} else {
    echo "文件下载失败。\n";
}

?>
登录后复制

代码解析:

小文AI论文
小文AI论文

轻松解决论文写作难题,AI论文助您一键完成,仅需一杯咖啡时间,即可轻松问鼎学术高峰!

小文AI论文 69
查看详情 小文AI论文
  • ssh2_connect():建立与SSH服务器的连接。
  • ssh2_auth_password():使用用户名和密码进行身份验证。
  • ssh2_sftp():获取SFTP子系统句柄,这是构建ssh2.sftp:// URI所必需的。
  • fopen($sftp_uri, 'r'):打开远程SFTP文件,返回一个可读的流资源。
  • fopen($local_file_name, 'w'):在本地创建一个新文件,并返回一个可写的流资源。
  • stream_copy_to_stream($remote_stream, $local_stream):这是核心步骤,它将远程流中的所有数据读取并写入到本地流中。
  • fclose():在操作完成后,务必关闭所有打开的文件流,释放资源。

解决方案二:更简洁高效的ssh2_scp_recv方法

如果你的服务器同时支持SCP(Secure Copy Protocol),那么PHP的ssh2扩展提供了一个更简洁、更直接的函数ssh2_scp_recv来下载文件。SCP通常与SFTP一同提供,并且在许多情况下,它能提供更快的传输速度。

ssh2_scp_recv(resource $session, string $remote_file, string $local_file): bool

这个函数直接将远程文件复制到本地文件,无需手动处理流。

以下是使用ssh2_scp_recv的示例:

<?php

// 假设这些变量已正确配置
$server_ip = '{my server ip}';
$username = '{user}';
$password = '{password}';
$remote_file_name = "test.txt"; // 远程服务器上的文件名
$local_file_name = "downloaded_test.txt"; // 本地保存的文件名
$remote_base_path = "/public_html"; // 远程文件所在的目录

// 1. 建立SSH连接
$connection = ssh2_connect($server_ip, 22);
if (!$connection) {
    die("无法连接到服务器。\n");
}

// 2. 进行身份验证
if (!ssh2_auth_password($connection, $username, $password)) {
    die("身份验证失败。\n");
}

// 3. 构建远程文件的完整路径
$remote_full_path = $remote_base_path . '/' . $remote_file_name;

// 4. 使用ssh2_scp_recv直接下载文件
if (ssh2_scp_recv($connection, $remote_full_path, $local_file_name)) {
    echo "文件 '" . $remote_file_name . "' 已成功通过SCP下载到本地 '" . $local_file_name . "'.\n";
} else {
    echo "文件SCP下载失败。\n";
}

// 注意:ssh2_scp_recv内部处理了文件的打开和关闭,无需手动fclose。
// 如果不再需要SSH连接,可以关闭它,尽管PHP脚本结束时会自动清理。
// ssh2_disconnect($connection); // PHP 8.0+ 可用

?>
登录后复制

代码解析:

  • ssh2_connect() 和 ssh2_auth_password():与第一种方法相同,用于建立和验证SSH连接。
  • ssh2_scp_recv($connection, $remote_full_path, $local_file_name):这是核心函数,它接收SSH连接资源、远程文件的完整路径和本地文件的保存路径作为参数,然后直接执行文件复制操作。成功返回true,失败返回false。

注意事项:

  • ssh2_scp_recv函数要求远程文件路径是服务器上的绝对路径或相对于用户主目录的路径。
  • 此方法依赖于服务器是否启用了SCP服务。大多数支持SFTP的服务器也会支持SCP,但并非绝对。
  • ssh2_scp_recv通常比手动处理流更为高效,因为它可能在底层使用了更优化的传输机制。

总结

当使用PHP的ssh2扩展从SFTP服务器下载文件时,切记fopen仅是打开文件流,并不会自动传输数据。为了确保文件内容能够正确下载到本地,你需要:

  1. 使用stream_copy_to_stream:这是最通用的方法,适用于所有SFTP连接,通过手动复制远程流数据到本地流。
  2. 使用ssh2_scp_recv:如果服务器支持SCP,这是更简洁高效的选择,只需一行代码即可完成文件下载。

在实际应用中,建议优先考虑ssh2_scp_recv,因为它代码量少、易于理解和维护。如果遇到SCP不支持的情况,再回退到stream_copy_to_stream方法。同时,务必加入适当的错误处理机制,以应对连接失败、认证失败或文件不存在等异常情况。

以上就是PHP SFTP文件传输:解决ssh2.sftp://下载空文件问题的详细内容,更多请关注php中文网其它相关文章!

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号