• 【PHP库】phpseclib - sftp远程文件操作


    需求场景说明

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

    安装

    composer require phpseclib/phpseclib:~3.0
    

    使用sftp功能

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

    1. return [
    2. 'sftp' => [
    3. 'host' => env('SFTP_HOST', '127.0.0.1'),
    4. 'port' => env('SFTP_PORT', 22),
    5. 'user' => env('SFTP_USE', null),
    6. 'password' => env('SFTP_PASSWORD', null),
    7. ],
    8. ];

    2.配置.env文件

    1. SFTP_HOST=127.0.0.1
    2. SFTP_PORT=22
    3. SFTP_USE=user
    4. SFTP_PASSWORD=password

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

    1. namespace App\Utils;
    2. use phpseclib3\Net\SFTP;
    3. class SftpHelper
    4. {
    5. private static $instance = [];
    6. public static function getInstance($key='sftp')
    7. {
    8. if (!isset(self::$instance[$key])) {
    9. $config = ConfigHelper::getInstance()->read('sftp.'.$key);
    10. self::$instance[$key] = new SFTP($config['host'], $config['port']);
    11. self::$instance[$key]->login($config['user'], $config['password']);
    12. }
    13. return self::$instance[$key];
    14. }
    15. }

    4.使用方法说明

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

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

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

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

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

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

    1. // 连接sftp服务器并登录
    2. $sftp = SftpHelper::getInstance('sftp');
    3. // 获取目录下的文件列表(不递归)
    4. $file_list = $sftp->nlist($remote_dir);
    5. // 循环文件列表,获取处理数据
    6. foreach ($file_list as $file_name) {
    7. // 跳过不处理的目录
    8. if (in_array($file_name, ['.', '..', 'Archive'])) {
    9. continue;
    10. }
    11. // 拼接完整的服务器文件路径
    12. $remote_file = $remote_dir.$file_name;
    13. // 设置本地存储的目录
    14. $save_path = env('FILE_PATH', '/data/storage/sftp/')."{$file_type}/";
    15. File::exists($save_path) or (File::makeDirectory($save_path, 0777, true) && @chmod($save_path, 0777));
    16. // 完整的本地路径
    17. $local_file = $save_path. $file_name;
    18. // 拉取sftp文件到本地目录
    19. if (!file_exists($local_file)) {
    20. if (!$sftp->is_readable($remote_file)) {
    21. $sftp->chmod('0777', $remote_file);
    22. }
    23. $sftp->get($remote_file, $local_file);
    24. }
    25. // 添加文件日志(同一个远程文件不重复拉取)
    26. // 后续可单独增加文件读取逻辑,使文件内容处理失败时可重复处理,并且不需要重复访问 sftp 服务器去读取远程文件
    27. SftpFile::updateOrCreate([
    28. 'remote_dir' => $remote_file,
    29. ], [
    30. 'action' => $file_type, // 文件类型
    31. 'filename' => $file_name, // 文件名
    32. 'filepath' => $local_file, // 本地服务器路径
    33. ]);
    34. // 日志创建成功之后再将文件移到Archive目录下,避免重复读取
    35. if (!$sftp->is_dir($remote_dir.'Archive/')) {
    36. // 没有则创建Archive目录
    37. $sftp->mkdir($remote_dir.'Archive/');
    38. }
    39. // 已读取的文件移到子目录Archive
    40. $sftp->rename($remote_file, "Archive/{$remote_file}");
    41. }

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

    1. // 读取待处理的文件列表
    2. $file_list = SftpFile::where([
    3. 'action' => $file_type,
    4. 'state' => 1
    5. ])->get();
    6. if (count($file_list) <= 0) {
    7. return;
    8. }
    9. // 连接sftp服务器并登录
    10. $mk_sftp = SftpHelper::getInstance('sftp');
    11. foreach ($file_list as $file) {
    12. // 校验推送的文件是否存在
    13. if (!file_exists($file->filepath)) {
    14. throw new ParamsException('推送的文件不存在');
    15. }
    16. $file_path = $file->filepath;
    17. $remote_file = $file->remote_dir;
    18. // 推送文件到sftp服务器
    19. // SFTP::SOURCE_LOCAL_FILE 表示以文件的形式,不设置时表示是按字符串形式上传
    20. $put_res = $mk_sftp->put($remote_file, $file_path, SFTP::SOURCE_LOCAL_FILE);
    21. if ($put_res) {
    22. $file->state = 1;
    23. $file->save();
    24. }
    25. }

    7.读取文件内容

    1. // 当前php.ini配置的是128M
    2. ini_set('memory_limit', '300M');
    3. $local_file = $file_info['filepath'];
    4. $remote_file = $file_info['remote_dir'];
    5. // 读取文件数据
    6. $fp = fopen($local_file, 'r');
    7. $file_data = [];
    8. while (!feof($fp)) {
    9. $row_str = fgets($fp); // 逐行读取。如果fgets不写length参数,默认是读取1k。
    10. $item = explode(',', trim($row_str));
    11. // 跳过表头
    12. // 将行数据转成指定的键值对
    13. }
    14. return $file_data;
  • 相关阅读:
    互联网医院系统:数字化时代中医疗服务的未来
    css3选择器用法合集
    射频功率放大器有哪些用途
    基于STM32G431嵌入式学习笔记——七、定时器定时
    记录一次慢SQL优化:大表关联小表->拆解为单表查询
    代码随想录二刷 Day 45
    1600页!今年BATJZ大厂Java面试题全面整理合集
    多测师肖sir_高级金牌讲师_python之函数007
    ES6中的set与map
    【译】.NET 7 中的性能改进(六)
  • 原文地址:https://blog.csdn.net/jh035/article/details/128062059