1. 程式人生 > >php中本地檔案拷貝至遠端伺服器的實現方式

php中本地檔案拷貝至遠端伺服器的實現方式

需要解決的問題:本地檔案搬移至遠端伺服器

前提:在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

多次嘗試都失敗,換為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之間建立信任關係不在這裡說明,會另外說明。