需要解决的问题:本地文件搬移至远程服务器

前提:在linux服务器之间拷贝

以下作为备忘和整理记录下来

因为在linux上拷贝文件,同事提醒用scp来做,搜索php scp之后发现php官方提供了使用scp向远程服务器拷贝文件以及相应的ssh连接方法。

使用方法如下:

$ip = XXX;
$port = XXX;
$user = XXX;
$pass = XXX;
$material_path = 'material_api.txt';
$target_path = '/data/aa/a';
$connection = ssh2_connect($ip, $port);
ssh2_auth_password($connection, $user, $pass);
$sftp = ssh2_sftp($connection);
$result = ssh2_scp_send($connection, $material_path, $target_path."/".$material_path, 0777);
echo "拷贝文件结果".$result."\n";




测试以上方法,报错

Call to undefined function ssh2_connect()


检查需要在服务器上安装ssh扩展,需要libssh2、ssh2。

安装扩展之后,测试上面代码成功。


实际需要拷贝的文件是视频文件,于是换为视频文件。

程序报错

Failed copying file
Failed copying file

多次尝试都失败,换为demo中的txt文件,执行成功。

猜测是文件类型或文件大小的原因。

找了一个小的视频文件,发现可以拷贝,文件大小增大之后,同样报错:Failed copying file

查看文件系统,复制的文件大小小于实际文件。

查看官方网站的说明,没有对ssh2_scp_send方法的传输文件的大小的说明。

再查网上只找到一个类似情况的提问,说遇到大文件的时候,拷贝文件失败。

后来在php官网看到用户贡献的使用方法中提到使用fopen、fwrite作为替代,方法如下:

<?php
$srcFile = '/var/tmp/dir1/file_to_send.txt';
$dstFile = '/var/tmp/dir2/file_received.txt';

// Create connection the the remote host
$conn = ssh2_connect('my.server.com', 22);

// Create SFTP session
$sftp = ssh2_sftp($conn);

$sftpStream = @fopen('ssh2.sftp://'.$sftp.$dstFile, 'w');

try {

    if (!$sftpStream) {
        throw new Exception("Could not open remote file: $dstFile");
    }
    
    $data_to_send = @file_get_contents($srcFile);
    
    if ($data_to_send === false) {
        throw new Exception("Could not open local file: $srcFile.");
    }
    
    if (@fwrite($sftpStream, $data_to_send) === false) {
        throw new Exception("Could not send data from file: $srcFile.");
    }
    
    fclose($sftpStream);
                    
} catch (Exception $e) {
    error_log('Exception: ' . $e->getMessage());
    fclose($sftpStream);
}
?>

测试小文件方法可用,100+MB的视频也可以拷贝,300+MB的视频同样不能拷贝成功,但没有错误信息。

后来在程序中打上log,发现拷贝大文件的时候程序执行到@file_get_contents($srcFile)就不向下执行,但也没有错误信息。

猜测file_get_contents()方法将文件内容读为一个字符串,当文件大小比较大时,方法处理时会有问题。

同事提醒php中有原生的执行命令的方法,可以试试直接使用命令拷贝文件。方法如下:

$user = XXX;
$ip = XXX;
$source_path= 'material_api.txt';
$target_path = '/data/aa/a';
$dest = $user."@".$ip.":".$target_path."/".$source_path;
exec("scp ".$source_path." ".$dest , $output, $return);


方法使用spc命令直接向远程服务器拷贝文件。

测试上述代码发现执行时,需要身份验证。

再通过资料查找建立了server1和server2之间的信任关系,使server1向server2执行scp不再需要每次都输入密码。

再测试以上代码,当拷贝成功的时候$return=0;失败的时候$return=1,完成了本地文件向远程服务器拷贝文件的要求。


ps:后期查找资料发现,对于file_get_contents(filename, use_iclude_path,context,offset,maxlen)读取大文件时,也许能够通过参数offset(文件读取的起始位置)、maxlen(读取的最大长度)的设置来分段读取文件。

如何在server1和server2之间建立信任关系不在这里说明,会另外说明。