4

【PHP库】phpseclib - sftp远程文件操作 - 程序小工

 1 year ago
source link: https://www.cnblogs.com/zqunor/p/16560180.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

需求场景说明

对接的三方商家需要进行文件传输,并且对方提供的方式是 sftp 的服务器账号,我们需根据他们提供的目录进行下载和上传指定文件。

composer require phpseclib/phpseclib:~3.0

使用sftp功能

1.新建并设置config/sftp.php文件


return [
    'sftp' => [
        'host'   => env('SFTP_HOST', '127.0.0.1'),
        'port'   => env('SFTP_PORT', 22),
        'user' => env('SFTP_USE', null),
        'password' => env('SFTP_PASSWORD', null),
    ],
];

2.配置.env文件

SFTP_HOST=127.0.0.1
SFTP_PORT=22
SFTP_USE=user
SFTP_PASSWORD=password

3.封装 app/Utils/SftpHelper.php调用库文件,通过单例可设置不同的 sftp 服务器

namespace App\Utils;

use phpseclib3\Net\SFTP;

class SftpHelper
{
    private static $instance = [];

    public static function getInstance($key='sftp')
    {
        if (!isset(self::$instance[$key])) {
            $config = ConfigHelper::getInstance()->read('sftp.'.$key);
            self::$instance[$key] = new SFTP($config['host'], $config['port']);
            self::$instance[$key]->login($config['user'], $config['password']);
        }

        return self::$instance[$key];
    }
}

4.使用方法说明

  • nlist:获取指定目录下的文件列表,包括子目录,(默认不会递归子目录下的文件)
  • is_readable: 判断文件是否有读权限
  • chmod:修改文件/目录权限,默认不递归
  • get:获取文件,默认获取文件内容。
  • is_dir:是否存在该目录
  • mkdir:创建目录
  • rename: 将文件重命名
  • put:上传文件

5.访问 sftp 服务器并下载文件到本地

5.1 读取指定服务器下的文件,并循环处理每个文件

5.2 下载远程文件到当前服务器的指定位置,并创建待处理文件记录表

说明:创建文件处理表可使文件读取逻辑失败时,可重复处理,并且不需要多次访问 sftp 服务器,进行逻辑解耦

5.3 创建文件记录数据后将服务器上的文件移到归档目录,避免重复读取

// 连接sftp服务器并登录
$sftp = SftpHelper::getInstance('sftp');
// 获取目录下的文件列表(不递归)
$file_list = $sftp->nlist($remote_dir);

// 循环文件列表,获取处理数据
foreach ($file_list as $file_name) {
    // 跳过不处理的目录
    if (in_array($file_name, ['.', '..', 'Archive'])) {
        continue;
    }

    // 拼接完整的服务器文件路径
    $remote_file = $remote_dir.$file_name;

    // 设置本地存储的目录
    $save_path = env('FILE_PATH', '/data/storage/sftp/')."{$file_type}/";
    File::exists($save_path) or (File::makeDirectory($save_path, 0777, true) && @chmod($save_path, 0777));

    // 完整的本地路径
    $local_file = $save_path. $file_name;
    // 拉取sftp文件到本地目录
    if (!file_exists($local_file)) {
        if (!$sftp->is_readable($remote_file)) {
            $sftp->chmod('0777', $remote_file);
        }

        $sftp->get($remote_file, $local_file);
    }

    // 添加文件日志(同一个远程文件不重复拉取)
    // 后续可单独增加文件读取逻辑,使文件内容处理失败时可重复处理,并且不需要重复访问 sftp 服务器去读取远程文件
    SftpFile::updateOrCreate([
        'remote_dir'  => $remote_file,
    ], [
        'action'     => $file_type, // 文件类型
        'filename'   => $file_name, // 文件名
        'filepath'   => $local_file, // 本地服务器路径
    ]);
    
    // 日志创建成功之后再将文件移到Archive目录下,避免重复读取
    if (!$sftp->is_dir($remote_dir.'Archive/')) {
        // 没有则创建Archive目录
        $sftp->mkdir($remote_dir.'Archive/');
    }

    // 已读取的文件移到子目录Archive
    $sftp->rename($remote_file, "Archive/{$remote_file}");
}

6.上传文件到 sftp 服务器的指定位置

// 读取待处理的文件列表
$file_list = SftpFile::where([
    'action' => $file_type,
    'state'  => 1
])->get();
if (count($file_list) <= 0) {
    return;
}

// 连接sftp服务器并登录
$mk_sftp = SftpHelper::getInstance('sftp');

foreach ($file_list as $file) {

    // 校验推送的文件是否存在
    if (!file_exists($file->filepath)) {
        throw new ParamsException('推送的文件不存在');
    }

    $file_path = $file->filepath;
    $remote_file = $file->remote_dir;

    // 推送文件到sftp服务器
    // SFTP::SOURCE_LOCAL_FILE 表示以文件的形式,不设置时表示是按字符串形式上传
    $put_res = $mk_sftp->put($remote_file, $file_path, SFTP::SOURCE_LOCAL_FILE);

    if ($put_res) {
        $file->state = 1;
        $file->save();
    }
}

7.读取文件内容

// 当前php.ini配置的是128M
ini_set('memory_limit', '300M');

$local_file = $file_info['filepath'];
$remote_file = $file_info['remote_dir'];

// 读取文件数据
$fp = fopen($local_file, 'r');

$file_data = [];
while (!feof($fp)) {

    $row_str = fgets($fp); // 逐行读取。如果fgets不写length参数,默认是读取1k。
    $item = explode(',', trim($row_str));
    
    // 跳过表头
   
    // 将行数据转成指定的键值对
}

return $file_data;


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK