需要解决的问题:本地文件搬移至远程服务器
前提:在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之间建立信任关系不在这里说明,会另外说明。