9

Slim 4 - League Flysystem v2 SFTP

 3 years ago
source link: https://odan.github.io/2021/01/03/slim4-sftp.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.
League Flysystem v2 SFTP

Daniel Opitz - Blog

Developer, Trainer, Open Source Contributor

Blog About me Donate

Slim 4 - League Flysystem v2 SFTP

Daniel Opitz

Daniel Opitz

03 Jan 2021

Table of contents

Requirements

Introduction

The HTTP protocol is great for transferring small amounts of data very quickly. That’s why most people prefer RESTful APIs for communication between servers.

You have to distinguish between small amounts of data that are transferred immediately and large amounts of data that are transferred with a time delay.

HTTP works very well as long as the amount of data is very small (< 5 MB) and there are no network timeouts.

In some rare use cases you may need to transfer more than 5 MB at once over the wire in a more delayed fashion, for example once a day or so. Then it would make sense to think about a specialized “file transfer protocol”, like SFTP (FTP over SSH).

SFTP or Secure File Transfer Protocol is also known as SSH File Transfer Protocol. It is a network protocol to be able to transfer files to remote systems.

SFTP allows companies to securely transfer billing and financial data or files for disaster recovery, for example. SFTP relies on SSH (Secure Shell) to transfer data. The client must authenticate itself to the server. Commands and data are encrypted. In this way, passwords and other sensitive information are not sent over the network in plain text and thus cannot be read or sniffed out.

SFTP was developed by the IETF (Internet Engineering Task Force) to allow files to be transferred and managed securely over TCP/IP networks.

SFTP uses the same commands as the traditional File Transfer Protocol (FTP).

Installation

The Flysystem SFTP Adapter provides a powerful filesystem and SFTP abstraction and uses Phpseclib to provide a convenient interface for SFTP file transfers.

To install Flysystem, run:

composer require league/flysystem

Before using the SFTP drivers, you will need to install the appropriate package via the Composer package manager:

composer require league/flysystem-sftp

Configuration

If you need to configure a SFTP filesystem, you may use the configuration example below:

use League\Flysystem\PhpseclibV2\SftpAdapter;
use League\Flysystem\PhpseclibV2\SftpConnectionProvider;
use League\Flysystem\UnixVisibility\PortableVisibilityConverter;

// ...

$settings['sftp'] = [
    'adapter' => new SftpAdapter(
        new SftpConnectionProvider(
            'localhost',
            'username',
            'password',
        ),
        '/', // remote root path
        PortableVisibilityConverter::fromArray(
            [
                'file' => [
                    'public' => 0640,
                    'private' => 0604,
                ],
                'dir' => [
                    'public' => 0740,
                    'private' => 7604,
                ],
            ]
        )
    ),
];

Container Setup

For autowiring I want to give the container a unique class name, because I my projects I have multiple Filesystem adapters e.g. one for the local filesystem and multiple SFTP adapters for other different servers. For this reason I extend a new class from \League\Flysystem\Filesystem. Then I add a container definition for that extended class, instead of using the too generic \League\Flysystem\Filesystem class. This allows you to explicitly declare the connection within the (service) class constructor and makes it possible to handle multiple different Filesystem “drivers” as simple as possible.

First create a new class in, src/Filesystem/SftpFilesystem.php.

<?php

namespace App\Filesystem;

use League\Flysystem\Filesystem;

final class SftpFilesystem extends Filesystem
{
    // must be empty
}

You could also give the class a less-generic name. For example when you have multiple “clients” you could give them names like AcmeSftpFilesystem, ContosoSftpFilesystem etc.

Insert a DI container definition for the SftpFilesystem:class in config/container.php:

<?php

use App\Filesystem\SftpFilesystem;
use League\Flysystem\Filesystem;
use Psr\Container\ContainerInterface;
// ...

return [

    // ...

    SftpFilesystem::class => function (ContainerInterface $container) {
        $sftp = $container->get('settings')['sftp'];

        return new SftpFilesystem($sftp['adapter']);
    },

];

Usage

To get the SftpFilesystem instance via dependency injection, just declare it in the constructor where you need it. Normally you should use the filesystem instance only within a domain or application service.

Example

<?php

namespace App\Domain\Example\Service;

use App\Filesystem\SftpFilesystem;

final class Example
{
    private SftpFilesystem $sftpFilesystem;

    public function __construct(SftpFilesystem $filesystem)
    {
        $this->sftpFilesystem = $filesystem;
    }

    public function foo(): void
    {
        // Reading a remote file
        $content = $this->sftpFilesystem->read('index.html');
        
        // ...
    }
}

Downloading (reading) files

Like writing a file, reading a file can be done in two ways. You can read the file contents in full as a string, or “stream” it by obtaining a resource. Using the resource allows you to stream the contents to a destination (local or to another filesystem) in order to keep memory usage low.

$content = $this->sftpFilesystem->read('index.html');

Uploading (writing) files

$this->sftpFilesystem->write('test.html', 'test');

List (remote) files

use League\Flysystem\StorageAttributes;
use League\Flysystem\FileAttributes;
use League\Flysystem\DirectoryAttributes;
// ...

/** @var StorageAttributes $item */
$items = $this->sftpFilesystem->listContents('/');

foreach ($items as $item) {
    $path = $item->path();

    if ($item instanceof FileAttributes) {
        // handle the file
    }
    
    if ($item instanceof DirectoryAttributes) {
        // handle the directory
    }
}

For more information about the usage, check out the Filesystem API documentation.

Conclusion

We have learned that, with a little trick, it’s very easy to use the league/flysystem package with multiple different Filesystem “drivers” in combination with an autowired DI container and dependency injection.

Read more

© 2021 Daniel Opitz | Twitter


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK