[Python] botoを使ってS3にファイルを保存する

ブログやWebサービスなどを運営する場合、データのバックアップは非常に重要です。その際、バックアップ先を複数用意しておくとより安心できます。この記事では、botoというPythonのライブラリを使って、Amazon Web ServicesのS3にデータを保存する方法を紹介します。

目次

  1. 基本的な使い方
  2. 指定ディレクトリをS3に保存する
  3. 今回使ったサンプル

基本的な使い方

今回のサンプルでは、S3への接続などはbotoという、AWSを操作するためのライブラリを使用するので、まずはこのモジュールをインストールします。インストールは、pipやeasy_installを使って簡単にできます。

sudo easy_install boto
OR
sudo pip install boto

botoを導入したら、以下のようなシンプルなコードで、S3にファイルを保存することができます。

# -*- coding: utf-8 -*-

import os
from boto.s3.connection import S3Connection
from boto.s3.connection import Location as S3Location
from boto.s3.key import Key

AWS_KEY_ID = '<AWS_KEY_ID>'
AWS_SECRET_KEY = '<AWS_SECRET_KEY>'
BUCKET_NAME = '18thtestbucket'
KEY_NAME = '18th_key'

def main():
    # アップロードするファイルを作成します。
    fname = 'test_basic_usage.txt'
    with open(fname, 'w') as f:
        f.write('basic_usage')

    # S3と接続します。
    c = S3Connection(AWS_KEY_ID, AWS_SECRET_KEY, host='s3-ap-northeast-1.amazonaws.com')

    # バケットを新規作成します。
    c.create_bucket(BUCKET_NAME, location=S3Location.APNortheast)

    # バケットを取得します。
    b = c.get_bucket(BUCKET_NAME)

    # キーを作成し、オブジェクトのS3上での名前を決めます。
    k = Key(b)
    k.key = KEY_NAME

    # S3に作成したファイルを保存します。
    k.set_contents_from_filename(fname)

    # S3からファイルを文字列として取り出します。
    print k.get_contents_as_string()

if __name__ == '__main__':
    main()

これを実行すると、BUCKET_NAMEで指定した「18th_sync_s3_test_bucket」に、KEY_NAMEで指定した「18th_key」という名前のオブジェクトがS3にアップロードされます。なお、<AWS_KEY_ID>と<AWS_SECRET_KEY>は、AWSの管理画面に表示されている文字列を使用します。

↑ 目次に戻る

指定ディレクトリをS3に保存する

上記基本的な使い方を少し発展させると、特定のディレクトリ以下のファイルをすべてS3に保存することも簡単にできます。これをcrontabなどに登録すると、指定したディレクトリ以下のファイルをすべて定期的に保存することができます。なお、botoでファイルを保存する際は、ファイル名に「/」で区切った文字列を指定すると、自動的にディレクトリを作ってくれます。

# -*- coding: utf-8 -*-

import logging
import os
import traceback
from optparse import OptionParser
from boto.s3.connection import S3Connection
from boto.s3. key import Key

AWS_KEY_ID = '<AWS_KEY_ID>'
AWS_SECRET_KEY = '<AWS_SECRET_KEY>'
LOG_FILEPATH = 'logs/sync_s3.log'

parser = OptionParser()
parser.add_option('-d', action='store', dest='target_dir_path')
parser.add_option('-b', action='store', dest='bucket_name')

logging.basicConfig(filename=LOG_FILEPATH, level=logging.INFO)

def main():
    """
    指定したディレクトリの中身とS3の同期を取るスクリプトです。
    """
    options, args = parser.parse_args()
    if not options.target_dir_path or not options.bucket_name:
        print 'Usage: python sync_s3.py -d <TARGET_DIR_PATH> -b <BUCKET_NAME>'
        exit()
    elif not os.path.exists(options.target_dir_path):
        print 'Target file path (%s) not found.' % options.target_dir_path
        exit()

    try:
        logging.info('%s start. | [target_dir_path: %s] [bucket_name: %s]' 
                     % (os.path.basename(__file__),
                        options.target_dir_path, options.bucket_name))

        # S3と接続をして、指定したディレクトリとS3の同期を取ります。
        sync_s3(options.target_dir_path, options.bucket_name)

        logging.info('%s end. | [target_dir_path: %s] [bucket_name: %s]' 
                     % (os.path.basename(__file__),
                        options.target_dir_path, options.bucket_name))

    except Exception, e:
        # 例外発生時はログに出力します。
        logging.error(traceback.format_exc(e))

def sync_s3(basedir, bucket_name):
    """
    DBのバックアップをS3に保存します。
    ファイル名と同じキーで保存されます。
    Args:
        basedir: S3と同期を取るディレクトリのパス
        bucket_name: 保存先のバケット名
    """
    c = S3Connection(AWS_KEY_ID, AWS_SECRET_KEY, host='s3-ap-northeast-1.amazonaws.com')
    b = c.get_bucket(bucket_name)

    fc = 0 # 同期を取ったファイル数を定義します。
    for abspath, relpath in iterfiles(basedir):
        k = Key(b)
        k.key = relpath
        k.set_contents_from_filename(abspath)
        fc += 1
    logging.info('%s files are uploaded.' % fc)

def iterfiles(basedir):
    """
    引数で指定されたディレクトリの中を調べ、パスを返します。
    Args:
        basedir: 処理対象ディレクトリ
    Returns:
        絶対パス
        basedirからの相対パス(basedir含む)
    """
    parent_dir = os.path.dirname(os.path.realpath(basedir))
    for (path, dirs, files) in os.walk(basedir):
        for fn in files:
            if fn.startswith('.'):
                # 隠しファイルは無視します。
                continue

            abspath = os.path.join(path, fn)
            yield abspath, os.path.relpath(abspath, parent_dir)

if __name__ == '__main__':
    main()

上記スクリプトでは2つの引数をとります。「-d」はS3にアップロードするディレクトリを指定し、「-b」はS3の保存先のバケット名を指定します。

python sync_s3.py -d ./uploads -b 18th_sync_s3_test_bucket

なお、このサンプルでは、バケットの作成自体は実施しない点には注意が必要です。また、ログを保存するディレクトリ「logs」もあらかじめ用意しておく必要があります。

↑ 目次に戻る

今回使ったサンプル

今回使ったサンプルは、ここからダウンロードすることができます。

サンプルの構成

  • basic_usage.py: ファイルをS3に保存するサンプル
  • sync_s3.py: 指定したディレクトリ以下のファイルをS3に保存するサンプル
  • logs/: ログ出力用ディレクトリ

↑ 目次に戻る

この記事が役に立った場合、シェアしていただけると励みになります!!