ftplib.FTP.storlines()

2025-06-06

ftplib.FTP.storlines() は、Pythonの ftplib モジュールが提供する FTP クラスのメソッドの一つで、FTPサーバーにファイルをアップロードするために使用されます。特に、このメソッドはテキストファイルをアップロードするのに適しています。

以下に詳しく説明します。

storlines() メソッドは、指定されたファイルオブジェクトからデータを読み込み、FTPサーバーに「行モード」で送信します。FTPにおける行モード(またはASCIIモード)転送では、改行コード(CR/LF)が自動的に変換されるため、異なるOS間でのテキストファイルの互換性を保つことができます。

バイナリファイル(画像、動画、実行ファイルなど)をアップロードする場合は、storlines() ではなく storbinary() メソッドを使用する必要があります。storbinary() はバイナリモードで転送を行うため、改行コードの変換は行われません。

storlines() メソッドのシグネチャ

storlines(cmd, fp, callback=None)
  • callback (オプション): 各行が送信された後に呼び出される関数です。コールバック関数は1つの引数(送信された行データ)を受け取ります。進捗表示などに利用できます。
  • fp: アップロードするファイルの内容が格納されているファイルオブジェクトです。このファイルオブジェクトは、readline() メソッドでデータを読み取れるように開かれている必要があります。通常は 'r' または 'rb' モードで開かれます(最近のPythonでは 'rb' が推奨されるケースもあります)。
  • cmd: FTPサーバーに送信するコマンド文字列です。通常は STOR filename の形式で指定します。filename はサーバー上の保存先ファイル名です。

使用例

テキストファイルをFTPサーバーにアップロードする基本的な例を以下に示します。

from ftplib import FTP

# FTPサーバーへの接続情報
FTP_HOST = "your_ftp_host"
FTP_USER = "your_username"
FTP_PASS = "your_password"

LOCAL_FILE_PATH = "local_text_file.txt"  # ローカルのアップロードしたいテキストファイル
REMOTE_FILE_NAME = "remote_text_file.txt" # サーバーに保存するファイル名

try:
    # FTPオブジェクトを作成し、接続
    ftp = FTP(FTP_HOST)
    print(ftp.getwelcome()) # サーバーからのウェルカムメッセージを表示

    # ログイン
    ftp.login(user=FTP_USER, passwd=FTP_PASS)
    print("ログインに成功しました。")

    # アップロードするファイルを読み取りモードで開く
    # テキストファイルの場合でも、環境によっては 'rb' (バイナリ読み込み) を推奨するケースがあります。
    # ただし、storlines()は内部で readline() を使うため、テキストデータとして扱われます。
    with open(LOCAL_FILE_PATH, 'rb') as fp: # 'rb'で開くのがより安全で一般的です
        # ファイルをアップロード
        # "STOR" コマンドに続けて、サーバー上でのファイル名を指定
        response = ftp.storlines(f"STOR {REMOTE_FILE_NAME}", fp)
        print(f"アップロード応答: {response}")

    print(f"{LOCAL_FILE_PATH}{REMOTE_FILE_NAME} としてFTPサーバーにアップロードしました。")

except Exception as e:
    print(f"エラーが発生しました: {e}")
finally:
    if 'ftp' in locals() and ftp:
        ftp.quit() # 接続を閉じる
        print("FTP接続を閉じました。")

  • エラーハンドリング: FTP通信はネットワークの状態やサーバーの設定に依存するため、エラーハンドリング(try-except-finally ブロック)を適切に行い、接続のクローズを忘れないようにすることが重要です。
  • バイナリファイル: 繰り返しになりますが、画像や実行ファイルなどのバイナリファイルをアップロードする場合は、storlines() ではなく storbinary() を使用してください。バイナリファイルを storlines() でアップロードすると、データが破損する可能性があります。
  • ファイルモード: storlines() は内部でファイルオブジェクトの readline() メソッドを使用するため、ファイルはテキストデータとして扱われます。ただし、Python 3ではファイルをバイナリモード('rb')で開くことが推奨されるケースがあります。これは、改行コードの自動変換を ftplib に任せるためです。'r' で開くと、Pythonのテキストモードがすでに改行変換を行うため、二重変換による問題が発生する可能性があります。


TypeError: a bytes-like object is required, not 'str'

原因
このエラーは、storlines() メソッドに渡すファイルオブジェクトがテキストモード(例: open('file.txt', 'r'))で開かれている場合に発生します。ftplib は内部でバイトデータを期待しているため、文字列を直接渡すとこのエラーが出ます。

トラブルシューティング
ファイルをバイナリモードで開いてください。'rb' (read binary) モードでファイルを開くのが正しい方法です。storlines() はテキストファイルを想定していますが、Python 3ではファイルの内容をバイトとして読み込み、ftplib 内部で適切な改行コード変換が行われます。

# 悪い例 (TypeErrorが発生する可能性あり)
# with open('local_text_file.txt', 'r') as fp:
#     ftp.storlines(f"STOR {REMOTE_FILE_NAME}", fp)

# 良い例
with open('local_text_file.txt', 'rb') as fp: # 'rb' で開く
    ftp.storlines(f"STOR {REMOTE_FILE_NAME}", fp)

ファイルの内容が壊れる(文字化け、改行コードの問題)

原因
storlines() はテキストファイル用ですが、WindowsとLinux/macOSでは改行コードの扱いが異なります(CRLF vs LF)。ftplib はASCIIモード転送を行うため、通常は自動的に改行コードを変換しますが、ファイルを開く際のエンコーディング指定や、storlines() 以外の方法(例: storbinary() をテキストファイルに使用)を用いると問題が発生することがあります。

トラブルシューティング

  • バイナリファイルの場合
    テキストファイルではない(画像、ZIPなど)場合は、storlines() ではなく、必ず storbinary() を使用してください。storbinary() はバイトデータをそのまま転送し、改行コード変換を行いません。
  • エンコーディングの確認
    ローカルファイルのエンコーディング(UTF-8, Shift_JISなど)がFTPサーバーの期待するエンコーディングと異なる場合、文字化けが発生する可能性があります。ftplib はファイルの内容のエンコーディングを自動的に判断しないため、サーバー側で解釈できない場合は文字化けの原因になります。もしサーバー側で特定のエンコーディングが要求される場合は、ファイル作成時にそのエンコーディングで保存し、'rb' で読み込むのが安全です。
  • storlines() を使う場合
    上記の通り、ファイルを 'rb' (read binary) モードで開くことを徹底してください。これにより、Pythonがファイルのバイトデータをそのまま ftplib に渡し、ftplib がFTPプロトコルに従って改行コードを適切に処理します。

ftplib.error_perm: 550 Permission denied など、FTPサーバーからのパーミッションエラー

原因

  • 指定されたディレクトリが存在しない。
  • 指定されたファイル名がサーバー側で既に存在し、上書きが許可されていない(または上書きできない状況にある)。
  • 指定されたリモートパスにファイルを書き込む権限がない。

トラブルシューティング

  • ファイル名
    サーバーに既に同名のファイルがある場合、上書きされるか、エラーになるかはFTPサーバーの設定によります。必要に応じて、一意のファイル名を生成したり、既存ファイルを削除してからアップロードすることを検討してください。
  • ディレクトリの存在確認と作成
    アップロード先のディレクトリがサーバー上に存在するか確認し、存在しない場合は ftp.mkd('directory_name') で作成するか、ftp.cwd('directory_name') で既存のディレクトリに移動してからアップロードしてください。
  • 権限の確認
    FTPサーバーにログインしているユーザーに、対象ディレクトリへの書き込み権限があるか確認してください。FTPクライアント(FileZillaなど)で手動でアップロードを試みて、権限の問題かを確認するのが有効です。

接続関連のエラー (ConnectionRefusedError, ftplib.error_temp, ftplib.error_perm, socket.timeout)

原因

  • アクティブモード/パッシブモードの問題。
  • タイムアウトが発生した(ネットワーク遅延、サーバーの応答が遅いなど)。
  • FTPサーバーがダウンしている、または一時的に利用できない。
  • ファイアウォールによって接続がブロックされている(ローカルまたはサーバー側)。
  • FTPサーバーに接続できない(IPアドレスやポートが間違っている)。

トラブルシューティング

  • パッシブモード/アクティブモード
    FTPにはデータ転送のための2つのモードがあります。
    • パッシブモード (Passive Mode - PASV)
      クライアントがサーバーにデータ接続用のポートを要求し、クライアントからサーバーへ接続します。これが今日のほとんどのファイアウォール環境で推奨されるモードです。ftplib はデフォルトでパッシブモードを使用します。
    • アクティブモード (Active Mode - PORT)
      サーバーがクライアントにデータ接続しに戻ってきます。クライアント側にファイアウォールがある場合、この接続がブロックされる可能性が高いです。 もしパッシブモードで問題が発生する場合、明示的にアクティブモードに切り替えることもできますが、これは通常ファイアウォールの問題を伴います。
    ftp.set_pasv(False) # アクティブモードに設定
    
    ただし、多くの場合、アクティブモードへの切り替えは新たなファイアウォール問題を引き起こすため、まずはパッシブモードでの設定(サーバー側でのポート開放など)を検討すべきです。
  • タイムアウト設定
    ftplib.FTP() コンストラクタや connect() メソッドに timeout パラメータを渡して、タイムアウト値を調整してみてください。
    ftp = FTP(FTP_HOST, timeout=60) # 60秒のタイムアウト
    
  • FTPサーバーの状態確認
    FTPサーバーが正常に稼働しているか確認してください。
  • ファイアウォールの確認
    ローカルPCやネットワークのファイアウォール、またはFTPサーバー側のファイアウォールがFTP接続(特にデータ転送ポート)をブロックしていないか確認してください。
  • ホスト名/IPアドレスとポートの確認
    接続情報が正しいか再確認してください。デフォルトのFTPポートは21です。

ftplib.error_proto や ftplib.error_reply

原因
これらはFTPサーバーから予期しない応答や、プロトコル仕様に合致しない応答が返された場合に発生します。サーバー側の設定ミスや、非標準のFTPサーバーである可能性、または通信の途中で問題が発生した場合に起こりえます。

  • FTPサーバーの互換性
    特定のFTPサーバーが標準から逸脱した動作をする場合、ftplib がうまく対応できないことがあります。その場合は、別のFTPライブラリの検討や、サーバー管理者に問い合わせる必要があるかもしれません。
  • サーバーのログ確認
    サーバー側のFTPログを確認することで、何が起こったのか、サーバーがどのようなエラーを返したのかを特定できる場合があります。
  • 簡単なファイルのアップロードでテスト
    まずは非常に小さな、単純なテキストファイルをアップロードしてみて、それが成功するかどうかを確認します。これにより、問題がファイルの内容やサイズにあるのか、それとも基本的な接続や認証にあるのかを切り分けることができます。
  • 接続とログインの確認
    storlines() を呼び出す前に、FTPサーバーへの接続 (FTP(), connect()) とログイン (login()) が正常に行われているかを確認してください。これらの前段階でエラーが発生している場合、storlines() は呼び出されません。
  • try-except ブロックでエラーを捕捉する
    ftplib.all_errors を捕捉することで、ftplib 固有のほとんどのエラーを処理できます。
    from ftplib import FTP, all_errors
    
    try:
        # ... FTP接続と操作 ...
    except all_errors as e:
        print(f"FTPエラーが発生しました: {e}")
    except Exception as e:
        print(f"予期せぬエラーが発生しました: {e}")
    finally:
        if 'ftp' in locals() and ftp:
            ftp.quit()
    


基本的なテキストファイルのアップロード

最も基本的な使用例です。ローカルにあるテキストファイルを、指定したファイル名でFTPサーバーにアップロードします。

from ftplib import FTP

# --- 設定情報 ---
FTP_HOST = "your_ftp_host.com" # FTPサーバーのホスト名またはIPアドレス
FTP_USER = "your_username"     # FTPユーザー名
FTP_PASS = "your_password"     # FTPパスワード

LOCAL_FILE_PATH = "my_document.txt" # ローカルのアップロードしたいファイル
REMOTE_FILE_NAME = "uploaded_doc.txt" # サーバーに保存するファイル名
# -----------------

try:
    # 1. FTPオブジェクトを作成し、サーバーに接続
    print(f"FTPサーバー {FTP_HOST} に接続中...")
    with FTP(FTP_HOST) as ftp:
        print(ftp.getwelcome()) # サーバーのウェルカムメッセージを表示

        # 2. ログイン
        ftp.login(user=FTP_USER, passwd=FTP_PASS)
        print("ログインに成功しました。")

        # 3. アップロードするファイルをバイナリ読み込みモードで開く
        # 'rb'モードで開くことで、改行コードの自動変換をftplibに任せます。
        with open(LOCAL_FILE_PATH, 'rb') as fp:
            print(f"ファイル '{LOCAL_FILE_PATH}' をアップロード中...")
            # 4. storlines() メソッドでファイルをアップロード
            # 第一引数はFTPコマンド(STOR <リモートファイル名>)
            # 第二引数は開いたファイルオブジェクト
            response = ftp.storlines(f"STOR {REMOTE_FILE_NAME}", fp)
            print(f"アップロード応答: {response}") # FTPサーバーからの応答を表示

        print(f"'{LOCAL_FILE_PATH}' を '{REMOTE_FILE_NAME}' として正常にアップロードしました。")

except Exception as e:
    print(f"エラーが発生しました: {e}")

# with ステートメントを使っているので、finallyでftp.quit()を明示的に呼び出す必要はありません
# with ステートメントがブロックを抜けるときに自動的に接続が閉じられます。

ポイント

  • open(LOCAL_FILE_PATH, 'rb') のように、必ずバイナリ読み込みモード ('rb') でファイルを開きます。これにより、WindowsとLinux/macOS間の改行コードの違いをftplibが適切に処理します。
  • FTP(FTP_HOST)with ステートメントで使用すると、処理が完了した後に自動的にFTP接続が閉じられます。

特定のディレクトリへのアップロードと進捗表示

ファイルを特定のサーバーディレクトリにアップロードし、さらに callback 関数を使ってアップロードの進捗状況を表示する例です。

from ftplib import FTP
import os # ファイルサイズの取得に使用

# --- 設定情報 ---
FTP_HOST = "your_ftp_host.com"
FTP_USER = "your_username"
FTP_PASS = "your_password"

LOCAL_FILE_PATH = "large_log_file.txt" # 少し大きめのテキストファイルを想定
REMOTE_DIR = "/remote/logs/" # アップロード先のサーバーディレクトリ
REMOTE_FILE_NAME = "server_log.txt"
# -----------------

# アップロードの進捗を表示するためのコールバック関数
def handle_progress(line):
    global uploaded_bytes # グローバル変数としてバイト数を管理
    uploaded_bytes += len(line)
    # 進捗率を表示(全体のファイルサイズがわかっている場合)
    if total_file_size > 0:
        progress_percentage = (uploaded_bytes / total_file_size) * 100
        print(f"\rアップロード中: {uploaded_bytes} bytes / {total_file_size} bytes ({progress_percentage:.2f}%)", end="")
    else:
        print(f"\rアップロード中: {uploaded_bytes} bytes", end="")

# グローバル変数の初期化
uploaded_bytes = 0
total_file_size = 0

try:
    print(f"FTPサーバー {FTP_HOST} に接続中...")
    with FTP(FTP_HOST) as ftp:
        ftp.login(user=FTP_USER, passwd=FTP_PASS)
        print("ログインに成功しました。")

        # リモートディレクトリへ移動
        try:
            ftp.cwd(REMOTE_DIR)
            print(f"ディレクトリ '{REMOTE_DIR}' に移動しました。")
        except Exception as e:
            print(f"ディレクトリ '{REMOTE_DIR}' が見つからないか、移動できません: {e}")
            print("現在のディレクトリにアップロードします。")
            REMOTE_DIR = "." # 現在のディレクトリを指す

        # ローカルファイルのサイズを取得(進捗表示のため)
        total_file_size = os.path.getsize(LOCAL_FILE_PATH)

        with open(LOCAL_FILE_PATH, 'rb') as fp:
            print(f"ファイル '{LOCAL_FILE_PATH}' をアップロード中...")
            # storlines() の第三引数にコールバック関数を指定
            response = ftp.storlines(f"STOR {REMOTE_FILE_NAME}", fp, callback=handle_progress)
            print(f"\nアップロード応答: {response}") # 応答を表示する前に改行

        print(f"'{LOCAL_FILE_PATH}' を '{os.path.join(REMOTE_DIR, REMOTE_FILE_NAME)}' として正常にアップロードしました。")

except Exception as e:
    print(f"エラーが発生しました: {e}")

ポイント

  • グローバル変数: 進捗を追跡するために uploaded_bytestotal_file_size をグローバル変数として使用していますが、より良い設計としては、クラスのインスタンス変数として管理することも考えられます。
  • callback=handle_progress: storlines()callback 引数に指定された関数は、ftplib がファイルから一行読み込み、サーバーに送信するたびに呼び出されます。これにより、進捗バーやアップロードされたバイト数をリアルタイムで表示できます。
  • ftp.cwd(REMOTE_DIR): アップロード先のディレクトリが存在しない場合、ftplib.error_perm などのエラーが発生します。この例では、エラーハンドリングを追加して、ディレクトリが見つからない場合に現在のディレクトリにアップロードするようにフォールバックしています。

ネットワークが不安定な場合や一時的なサーバーの問題が発生した場合に備え、エラーハンドリングを強化し、一定回数リトライするロジックを追加した例です。

from ftplib import FTP, all_errors
import time

# --- 設定情報 ---
FTP_HOST = "your_ftp_host.com"
FTP_USER = "your_username"
FTP_PASS = "your_password"

LOCAL_FILE_PATH = "retry_test_file.txt"
REMOTE_FILE_NAME = "retry_upload.txt"

MAX_RETRIES = 3    # 最大リトライ回数
RETRY_DELAY = 5    # リトライ間の待機時間(秒)
# -----------------

def upload_file_with_retry(ftp_host, ftp_user, ftp_pass, local_path, remote_name, max_retries, retry_delay):
    for attempt in range(1, max_retries + 1):
        try:
            print(f"--- アップロード試行 {attempt}/{max_retries} ---")
            with FTP(ftp_host) as ftp:
                ftp.login(user=ftp_user, passwd=ftp_pass)
                print("ログイン成功。")

                with open(local_path, 'rb') as fp:
                    response = ftp.storlines(f"STOR {remote_name}", fp)
                    print(f"アップロード応答: {response}")
                print(f"'{local_path}' を '{remote_name}' として正常にアップロードしました。")
                return True # 成功したらループを終了

        except all_errors as e:
            print(f"FTPエラーが発生しました: {e}")
            if attempt < max_retries:
                print(f"リトライします。{retry_delay}秒待機...")
                time.sleep(retry_delay)
            else:
                print("最大リトライ回数に達しました。アップロードに失敗しました。")
                return False
        except Exception as e:
            print(f"予期せぬエラーが発生しました: {e}")
            return False
    return False # すべてのリトライが失敗した場合

# テスト用のダミーファイルを作成(存在しない場合)
if not os.path.exists(LOCAL_FILE_PATH):
    with open(LOCAL_FILE_PATH, 'w') as f:
        f.write("This is a test file for retry mechanism.\n")
        f.write("Line 2.\n")
        f.write("Line 3.\n")

# 関数を呼び出してアップロードを実行
if upload_file_with_retry(FTP_HOST, FTP_USER, FTP_PASS, LOCAL_FILE_PATH, REMOTE_FILE_NAME, MAX_RETRIES, RETRY_DELAY):
    print("\nファイルアップロード処理が正常に完了しました。")
else:
    print("\nファイルアップロード処理が失敗しました。")
  • time.sleep(): リトライの間に一定時間待機することで、サーバーへの負荷を減らし、一時的なネットワークの問題が解決するのを待ちます。
  • リトライループ: for attempt in range(...) ループを使って、指定された回数だけアップロード処理を試行します。
  • ftplib.all_errors: ftplib 関連のすべてのエラーを捕捉するための便利な例外クラスです。


ftplib.FTP.storbinary() の使用

storlines() はテキストファイル(ASCIIモード)のアップロードに適していますが、storbinary()バイナリファイル(画像、動画、ZIPファイルなど)のアップロードに適しています。ただし、テキストファイルをバイナリモードでアップロードしたい場合にも使用できます。

特徴

  • シグネチャ
    storbinary(cmd, fp, blocksize=8192, callback=None)
    • cmd: STOR filename または APPE filename (追記) などのFTPコマンド。
    • fp: バイナリ読み込みモード ('rb') で開かれたファイルオブジェクト。
    • blocksize: 一度に読み書きするバイト数。大きいほど高速になる可能性がありますが、メモリ使用量も増えます。
    • callback: 進捗表示用のコールバック関数。
  • 動作
    ファイルの内容をバイト列として読み込み、FTPサーバーにそのまま送信します。改行コードの自動変換は行いません。
  • 用途
    バイナリファイルのアップロードが主ですが、テキストファイルをバイト列として扱い、改行コード変換を抑制したい場合にも使用できます。

storbinary() を使ったテキストファイルのアップロード例

from ftplib import FTP

FTP_HOST = "your_ftp_host.com"
FTP_USER = "your_username"
FTP_PASS = "your_password"

LOCAL_FILE_PATH = "my_plain_text.txt" # ローカルのテキストファイル
REMOTE_FILE_NAME = "uploaded_binary_text.txt" # サーバー上のファイル名

try:
    with FTP(FTP_HOST) as ftp:
        ftp.login(user=FTP_USER, passwd=FTP_PASS)
        print("ログインに成功しました。")

        # storbinary() を使ってテキストファイルをアップロードする場合も、'rb' で開きます。
        # storlines() とは異なり、改行コードの自動変換は行われません。
        # ただし、最近のftplibではstorlines()も内部で'rb'を推奨しているため、
        # どちらを使うかは、改行コード変換の有無を意識するかどうかで選択できます。
        with open(LOCAL_FILE_PATH, 'rb') as fp:
            print(f"ファイル '{LOCAL_FILE_PATH}' を storbinary() でアップロード中...")
            response = ftp.storbinary(f"STOR {REMOTE_FILE_NAME}", fp)
            print(f"アップロード応答: {response}")

        print(f"'{LOCAL_FILE_PATH}' を '{REMOTE_FILE_NAME}' として正常にアップロードしました (storbinary)。")

except Exception as e:
    print(f"エラーが発生しました: {e}")

storlines() と storbinary() の使い分け

  • storbinary(): バイナリファイル、またはテキストファイルをバイト列としてそのまま転送したい場合(改行コード変換が不要な場合)
  • storlines(): テキストファイル(改行コードの自動変換が必要な場合)

実際には、Python 3ではstorlines()でも'rb'でファイルを開くのが推奨されるため、多くのテキストファイルアップロードのシナリオではstorlines()storbinary()の挙動上の違いは小さくなっています。しかし、storbinary()blocksizeを指定できる点で、大きなファイルの転送性能を調整する柔軟性があります。

ftplib はPython標準ライブラリであり、基本的なFTP機能を提供しますが、より高レベルな機能(例: ファイルの存在チェック、ディレクトリの再帰的なアップロード、セッション管理、エラーハンドリングの簡素化)や、よりモダンな接続方法(例: FTPS, SFTP)が必要な場合は、外部ライブラリの利用が非常に有効です。

a. paramiko (SFTPに特化)

paramiko は、SSHv2プロトコルを実装しており、SFTP(SSH File Transfer Protocol)を扱うためのデファクトスタンダードともいえるライブラリです。SFTPはFTPとは異なるプロトコルですが、セキュアなファイル転送が必要な場合に検討する価値があります。

特徴

  • 依存関係
    pycryptodome (または pycrypto) などの暗号ライブラリに依存します。
  • 機能
    ファイルのアップロード/ダウンロード、ディレクトリ操作、パーミッション変更など、豊富な機能を提供します。
  • セキュリティ
    SSHの暗号化を利用するため、FTPよりもはるかにセキュアです。

インストール
pip install paramiko

paramiko を使ったSFTPファイルアップロード例

import paramiko

# --- 設定情報 ---
SFTP_HOST = "your_sftp_host.com" # SFTPサーバーのホスト名
SFTP_PORT = 22                 # SFTPのデフォルトポートは22
SFTP_USER = "your_username"
SFTP_PASS = "your_password"
# または、キーファイル認証
# PRIVATE_KEY_PATH = "/path/to/your/private_key"

LOCAL_FILE_PATH = "my_secure_document.txt"
REMOTE_FILE_PATH = "/remote/path/secure_doc.txt"
# -----------------

try:
    with paramiko.SSHClient() as client:
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 初回接続時にホストキーを追加
        print(f"SFTPサーバー {SFTP_HOST}:{SFTP_PORT} に接続中...")

        # パスワード認証
        client.connect(hostname=SFTP_HOST, port=SFTP_PORT, username=SFTP_USER, password=SFTP_PASS)

        # または、キーファイル認証
        # client.connect(hostname=SFTP_HOST, port=SFTP_PORT, username=SFTP_USER, key_filename=PRIVATE_KEY_PATH)

        print("SFTP接続に成功しました。")

        with client.open_sftp() as sftp:
            print(f"ファイル '{LOCAL_FILE_PATH}' を '{REMOTE_FILE_PATH}' にアップロード中...")
            sftp.put(LOCAL_FILE_PATH, REMOTE_FILE_PATH)
            print("アップロードが正常に完了しました。")

except Exception as e:
    print(f"SFTP接続またはアップロード中にエラーが発生しました: {e}")

ポイント

  • paramiko はより高レベルなAPIを提供し、ファイルの転送がput()メソッド一つで完結します。
  • SFTPはFTPとは根本的に異なるプロトコルであるため、SFTPサーバーにしか接続できません。

b. ftputil (より高レベルなFTPラッパー)

ftputilftplib の上に構築された高レベルなFTPクライアントライブラリで、ローカルファイルシステムとFTPサーバーのファイルシステムをより一貫性のある方法で扱えるように設計されています。

特徴

  • 機能
    ディレクトリの再帰的なアップロード/ダウンロード、ファイルの同期、ファイルのタイムスタンプ保持など、多くの便利な機能があります。
  • 堅牢性
    内部で再接続やエラーハンドリングを自動的に行い、より堅牢なFTPセッションを提供します。
  • 使いやすさ
    os モジュールや shutil モジュールに似たAPIを提供し、直感的に使用できます。

インストール
pip install ftputil

ftputil を使ったファイルアップロード例

import ftputil

# --- 設定情報 ---
FTP_HOST = "your_ftp_host.com"
FTP_USER = "your_username"
FTP_PASS = "your_password"

LOCAL_FILE_PATH = "my_ftputil_doc.txt"
REMOTE_FILE_PATH = "/remote/path/ftputil_doc.txt"
# -----------------

try:
    print(f"FTPサーバー {FTP_HOST} に接続中 (ftputil)...")
    # ftputil.FTPHost は with ステートメントで自動的に接続を閉じます
    with ftputil.FTPHost(FTP_HOST, FTP_USER, FTP_PASS) as host:
        print("ログインに成功しました。")

        print(f"ファイル '{LOCAL_FILE_PATH}' を '{REMOTE_FILE_PATH}' にアップロード中...")
        # upload() メソッドを使用。バイナリ転送がデフォルト
        host.upload(LOCAL_FILE_PATH, REMOTE_FILE_PATH)
        print("アップロードが正常に完了しました。")

except Exception as e:
    print(f"FTP接続またはアップロード中にエラーが発生しました: {e}")
  • upload() メソッド一つでファイル転送が完了するため、ftplibopen()storlines() / storbinary() の組み合わせよりもコードが簡潔になります。
  • ftputil.FTPHost は自動的にバイナリモードでファイルを転送します。テキストファイルをアップロードする場合でも、内部で適切に処理されます。
代替方法主な用途特徴セキュリティ
ftplib.FTP.storbinary()バイナリファイル、バイト列としてのテキストftplib 標準機能、改行コード変換なし、blocksize 指定可能
paramiko (SFTP)セキュアなファイル転送 (SSH経由)高度なセキュリティ、SSHベース、パスワード/キー認証、SFTP機能が豊富
ftputil高レベルなFTP操作ftplibのラッパー、使いやすいAPI、堅牢、再帰的アップロード/同期など低 (FTPのまま)
  • より高レベルなFTP操作や堅牢性
    ftputil が非常に便利です。
  • セキュリティが最優先
    paramiko を使ったSFTPを検討してください。FTPサーバーがSFTPに対応している必要があります。
  • バイナリファイルのアップロード
    ftplib.FTP.storbinary() を使用すべきです。
  • シンプルなFTPテキストファイルアップロード
    ftplib.FTP.storlines() または ftplib.FTP.storbinary() ('rb'モードでファイルを開く) が適切です。