Pythonを使ったSFTPへの接続、ファイルのリストアップ、アップロード、ダウンロードの方法についてご説明します。

安全なファイルやデータの転送を目的とするSFTPは、多くのユーザーにとって最良の選択肢となっています。SFTPは効率的でユーザーに優しいプロトコルですが、Pythonのようなプログラミング言語からSFTPサーバーに接続するには、内容をもう少し深掘りする必要があります。以下の記事を参考に、PythonからSFTPを利用し、接続できるようになればと思います。

最大限のセキュリティと信頼性を備えたSFTP To Go
SFTP To Goは、管理されているSFTP/FTPS/S3をサービスとして提供します - 最高の信頼性、セキュリティ、可用性、1分のセットアップで。あらゆる規模の企業に最適です。
今すぐSFTP To Goをお試しに!

必要条件

この作業に取り込む前に、設定がいくつか必要です。ここでは 'pysftp' パッケージを使って SFTP サーバに接続し、コマンドを渡します。インストールの準備ができたら、以下のコマンドを手動で実行します:

pip install pysftp

または、『requirements.txt』の ファイルを作成し、その中で依存関係を宣言します。そして、以下の内容を『requirements.txt』として保存します:

pysftp>=0.2.9

そして以下のように実行します:

pip install -r requirements.txt

SFTPへの接続

この記事では、sftp://user:password@host という URI 形式で SFTP サーバーに接続するのに必要な情報がすべて格納されているSFTPTOGO_URLという環境変数を使用します。この変数は `urlparse` を使って URI の部分を抽出し、リモートサーバーのホスト鍵は~/.ssh/known_hosts.を使ってデフォルトで検証されます。

接続が確立されると、SFTP クライアントオブジェクトが変数connectionに代入されます。

import pysftp
from urllib.parse import urlparse
import os


class Sftp:
    def __init__(self, hostname, username, password, port=22):
        """Constructor Method"""
        # Set connection object to None (initial value)
        self.connection = None
        self.hostname = hostname
        self.username = username
        self.password = password
        self.port = port

    def connect(self):
        """Connects to the sftp server and returns the sftp connection object"""

        try:
            # Get the sftp connection object
            self.connection = pysftp.Connection(
                host=self.hostname,
                username=self.username,
                password=self.password,
                port=self.port,
            )
        except Exception as err:
            raise Exception(err)
        finally:
            print(f"Connected to {self.hostname} as {self.username}.")


    def disconnect(self):
        """Closes the sftp connection"""
        self.connection.close()
        print(f"Disconnected from host {self.hostname}")

ファイルのリストアップ

接続ができたので、それを使ってリモート SFTP サーバー上のファイル一覧が取得できます。これは、接続オブジェクトのlistdir関数またはlistdir_attr関数の呼び出しで行われます。これらの関数の引数にリモートパスか引数の指定なしによって、現在のリモートディレクトリのファイルとディレクトリの一覧を表示します。listdir 関数はファイル名のリスト(文字列)を返し、listdir_attr 関数はファイル名に加え、ファイルサイズ、作成、更新のタイムスタンプ、パーミッションを含む SFTPAttributes オブジェクトのリストを返します。この例では、ラッパー関数がジェネレータを返し、関数呼び出し側が返されたファイル一覧を反復処理できるようにしています。

    def listdir(self, remote_path):
        """lists all the files and directories in the specified path and returns them"""
        for obj in self.connection.listdir(remote_path):
            yield obj

    def listdir_attr(self, remote_path):
        """lists all the files and directories (with their attributes) in the specified path and returns them"""
        for attr in self.connection.listdir_attr(remote_path):
            yield attr

ファイルのアップロード

次のステップは、ファイルのアップロードです。接続オブジェクトのput関数を使って、ローカルファイルへのパスと、アップロード終了時にファイルが置かれるべき場所であるリモートパスを渡します。関数の呼び出しは次のようになります:connection.put("./local.txt", "./remote.txt")

    def upload(self, source_local_path, remote_path):
        """
        Uploads the source files from local to the sftp server.
        """

        try:
            print(
                f"uploading to {self.hostname} as {self.username} [(remote path: {remote_path});(source local path: {source_local_path})]"
            )

            # Download file from SFTP
            self.connection.put(source_local_path, remote_path)
            print("upload completed")

        except Exception as err:
            raise Exception(err)

ファイルのダウンロード

最後は、ファイルのダウンロードです。接続オブジェクトの get 関数を使って、リモートファイルへのパスと、ダウンロードしたファイルを保存するローカルパスを渡します。この関数は、次のように呼び出します: connection.get("./remote.txt", "./download.txt")

    def download(self, remote_path, target_local_path):
        """
        Downloads the file from remote sftp server to local.
        Also, by default extracts the file to the specified target_local_path
        """

        try:
            print(
                f"downloading from {self.hostname} as {self.username} [(remote path : {remote_path});(local path: {target_local_path})]"
            )

            # Create the target directory if it does not exist
            path, _ = os.path.split(target_local_path)
            if not os.path.isdir(path):
                try:
                    os.makedirs(path)
                except Exception as err:
                    raise Exception(err)

            # Download from remote sftp server to local
            self.connection.get(remote_path, target_local_path)
            print("download completed")

        except Exception as err:
            raise Exception(err)

まとめ

というわけで、ゴールまで辿り着きました! もし、このプログラムを最初から最後まで実行したい場合は、次のコードをコピーして main.pyとして保存してください。

import pysftp
from urllib.parse import urlparse
import os


class Sftp:
    def __init__(self, hostname, username, password, port=22):
        """Constructor Method"""
        # Set connection object to None (initial value)
        self.connection = None
        self.hostname = hostname
        self.username = username
        self.password = password
        self.port = port

    def connect(self):
        """Connects to the sftp server and returns the sftp connection object"""

        try:
            # Get the sftp connection object
            self.connection = pysftp.Connection(
                host=self.hostname,
                username=self.username,
                password=self.password,
                port=self.port,
            )
        except Exception as err:
            raise Exception(err)
        finally:
            print(f"Connected to {self.hostname} as {self.username}.")

    def disconnect(self):
        """Closes the sftp connection"""
        self.connection.close()
        print(f"Disconnected from host {self.hostname}")

    def listdir(self, remote_path):
        """lists all the files and directories in the specified path and returns them"""
        for obj in self.connection.listdir(remote_path):
            yield obj

    def listdir_attr(self, remote_path):
        """lists all the files and directories (with their attributes) in the specified path and returns them"""
        for attr in self.connection.listdir_attr(remote_path):
            yield attr

    def download(self, remote_path, target_local_path):
        """
        Downloads the file from remote sftp server to local.
        Also, by default extracts the file to the specified target_local_path
        """

        try:
            print(
                f"downloading from {self.hostname} as {self.username} [(remote path : {remote_path});(local path: {target_local_path})]"
            )

            # Create the target directory if it does not exist
            path, _ = os.path.split(target_local_path)
            if not os.path.isdir(path):
                try:
                    os.makedirs(path)
                except Exception as err:
                    raise Exception(err)

            # Download from remote sftp server to local
            self.connection.get(remote_path, target_local_path)
            print("download completed")

        except Exception as err:
            raise Exception(err)

    def upload(self, source_local_path, remote_path):
        """
        Uploads the source files from local to the sftp server.
        """

        try:
            print(
                f"uploading to {self.hostname} as {self.username} [(remote path: {remote_path});(source local path: {source_local_path})]"
            )

            # Download file from SFTP
            self.connection.put(source_local_path, remote_path)
            print("upload completed")

        except Exception as err:
            raise Exception(err)


if __name__ == "__main__":
    sftp_url = os.environ.get("SFTPTOGO_URL")

    if not sftp_url:
        print("First, please set environment variable SFTPTOGO_URL and try again.")
        exit(0)

    parsed_url = urlparse(sftp_url)

    sftp = Sftp(
        hostname=parsed_url.hostname,
        username=parsed_url.username,
        password=parsed_url.password,
    )

    # Connect to SFTP
    sftp.connect()

    # Lists files with attributes of SFTP
    path = "/"
    print(f"List of files with attributes at location {path}:")
    for file in sftp.listdir_attr(path):
        print(file.filename, file.st_mode, file.st_size, file.st_atime, file.st_mtime)

    # Upload files to SFTP location from local
    local_path = "/Users/saggi/Downloads/tls2.png"
    remote_path = "/tls2.png"
    sftp.upload(local_path, remote_path)

    # Lists files of SFTP location after upload
    print(f"List of files at location {path}:")
    print([f for f in sftp.listdir(path)])

    # Download files from SFTP
    sftp.download(
        remote_path, os.path.join(remote_path, local_path + '.backup')
    )

    # Disconnect from SFTP
    sftp.disconnect()

最後に、以下のコマンドで実行します:

python main.py

Pythonを使ったSFTPへの接続完了です。お疲れ様でした。

最大限のセキュリティと信頼性を備えたSFTP To Go
SFTP To Goは、管理されているSFTP/FTPS/S3をサービスとして提供します - 最高の信頼性、セキュリティ、可用性、1分のセットアップで。あらゆる規模の企業に最適です。
今すぐSFTP To Goをお試しに!