技術的な話

Python - Azure Blob Storageからデータをダウンロード/アップロードしてみる(Pythonコード書く編)

え?使わない?きっと使う時がくるさ!

何でも入るBlob StorageはAWSもGCPにもあるんだよ?使わなきゃ損!

安いので大容量データの格納だったり、一時的な格納スペースとして活用されているのかな?

個人的なハマりポイントもまとめながら手順を説明してきます。

最終目標

  • Azure Blob Storageからファイルをダウンロード/アップロードする

前回までのあらすじ

https://buzz-server.com/2021/01/30/azure-blob-storage/

この記事でできること

Pythonについては次の投稿でまとめます。

  • Azure Blob Storageの作成

環境条件

今回は以下の条件で進めます。

  • Python 3.9.1
  • Azureのアカウントがあること(無料枠有もしくはクレカ登録済)

Python - コードを書く

どんな処理を書くか?

  • storageというContainerを作成
  • storageというContainer内のinputフォルダ内の全てのファイルをダウンロード
  • ダウンロードしたファイルをstorageというContainer内のoutputフォルダ内に全てアップロード

ざっくりこんな感じのことを実現しようと思います。

どういう感じて書くか?

  1. 必要なモジュールをインストール
  2. データをダウンロードするクラスを作成
  3. データをアップロードするクラスを作成
  4. main.pyに必要な処理を記載する

必要なモジュールをインストール

$ pip install azure-blob-storage
https://docs.microsoft.com/ja-jp/azure/storage/blobs/storage-quickstart-blobs-python

Azure Blob Storageからデータをダウンロードするクラス

データをダウンロードする処理をまとめたクラスを作成します。

ソース上はBlobにアクセスする部分とローカル部分にファイルを作成する部分に分けています。

Blobアクセス箇所

  def download_blob(self):
    my_blobs = self.my_container.list_blobs()
    self._logger.info(f'Download target blob : {os.path.join(self.target_folder, "*")}')
    for blob in my_blobs:
        if blob.name.startswith(self.target_folder) :
            dirname, filename = os.path.split(blob.name)
            bytes = self.my_container.get_blob_client(blob).download_blob().readall()
            self.save_blob(blob.name, bytes)
        else:
            self._logger.debug(f'Ignore blob : {blob.name}')

ローカルファイル作成箇所

  def save_blob(self, file_name, file_content):
    os.makedirs(os.path.dirname(file_name), exist_ok=True)
 
    with open(file_name, "wb") as file:
      file.write(file_content)

Azure Blob Storageからデータをアップロードするクラス

データをアップロードする処理をまとめたクラスを作成します。

微妙にダウンロードと処理の書き方が違います。

  def upload_local_file(self):
      try:
          files = glob.glob(os.path.join(self.source_folder, '*'))
          # local file read
          for file in files:
            # self._logger.info(f'Delete blob file :{os.path.join(self.target_folder, os.path.basename(file))}')
            # blob_client.delete_blob()
            blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=os.path.join(self.target_folder, os.path.basename(file)))

            self._logger.info(f'Upload local file :{os.path.join(self.target_folder, os.path.basename(file))}')
            with open(file, "rb") as data:
                blob_client.upload_blob(data)

      except ResourceExistsError as ex:
          self._logger.error(f"[ResourceExistsError][{self.__module__}]:{ex.message}")
      except ResourceNotFoundError as ex:
          self._logger.error(f"[ResourceNotFoundError][{self.__module__}]:{ex.message}")
      except Exception as ex:
          self._logger.error(f"[ExceptionError][{self.__module__}]:{ex.message}")

アップロード先にファイルが存在する場合はResourceExistsエラーが発生します。

回避方法としては事前にBlobをダウンロードして、リネームして置いておくぐらいでしょうか。

ちなみにbdelete_blob()メソッドを走らせても同様のエラーが発生します。

main.pyに処理を書きます

上記2つのクラスをmain.pyに書いていきます。

main.pyの上部に前回作成したAzure Blob Storageの接続文字列やコンテナ名称などを記載します。

connection_string:str = r'<your connection string>'
container_name:str = r'<your container name>'
download_path:str = r'<your download folder path>'
upload_path:str = r'<your upload folder path>'

接続文字列定義後はクラスを呼び出して、各メソッドをそれぞれ呼び出しているだけです。

  azure_blob_file_downloader = AzureBlobFileDownloader(connection_string, container_name, download_path)
  azure_blob_file_downloader.download_blob()
  azure_blob_file_uploader = AzureBlobFileUploader(connection_string, container_name, upload_path, download_path)
  azure_blob_file_uploader.upload_local_file()

全てのソースは下記にあります。(色々Loggerとか使用していますが不要であれば削除してください。)

https://github.com/ktkd0222/azure-blob-storage

処理結果

VSCodeのターミナル上から実行するとこんな形になります。

アップロード先に既に同じファイルが存在する場合は下記のようなエラーが発生します。

その際はAzure Blob Storage上からファイルを削除してください。

inputフォルダにあるinput.txtが、

outputフォルダにコピーされます。

以上

-技術的な話
-, ,