S3上のマルチバイトファイル名でハマった話

2020.02.19

はじめに

データアナリティクス事業本部のkobayashiです。

先日S3にファイル名がマルチバイトであるファイルをPythonで扱おうとした際にdownload_file()で目的のファイルをダウンロードしようと試みたのですがエラーになってしまい暫くハマってしまいました。

あまりファイル名がマルチバイトを扱うことがないのでおそらく原因はファイル名だろうと当たりはついていたのですがなかなか解決しなかったの自戒を込めてまとめます。

環境

  • macOS Mojave
  • Python 3.6.5
  • boto3 1.10.22

起こった事象

開発の中で以下のようなコードでファイルをダウンロードしようとしていました。

import boto3

s3_client = boto3.client("s3")
s3_client.download_file("hoge", "サンプルデータ.csv", "サンプルデータ.csv")

しかし実行してもファイルが存在しないと怒られてしましました。

# python test.py

An error occurred (404) when calling the HeadObject operation: Not Found

目的のファイルはマネージメントコンソール経由でS3にアップロードしたので存在しないはずはなかったのですが何度実行してもファイルが存在しないと怒られてしましました。

そこでlist_objects_v2()でバケット内のファイルを取得してみると

import boto3

s3_client = boto3.client("s3")
result = s3_client.list_objects_v2(Bucket="hoge")
print([v["Key"] for v in result["Contents"]])

のような形で濁音と半濁音が分離していることがわかりました。

そこでダウンロードするスクリプト中でオブジェクト名を上記と同じように濁音・半濁音を分離した状態で指定すると無事に取得できました。

Unicode正規化方式

そこでこの現象について色々調べているとMacOS固有の現象で、正規化方法が「NFD」であることが原因であるとわかり、更に調べるとAmazon S3のUnicode正規化についていろいろと試してみたの記事にたどり着きました。

そこでこの記事を参考にさせていただきながら色々なパターンでS3にファイルをアップロードしてテストを行いました。

アップロード方法を変更して検証

使用するファイルはFinder上で「ばびぶべぼぱぴぷぺぽバビブベボパピプペポ.txt」のファイル名を付けて保存します。 そしてこのファイルをMacOSから

  • マネージメントコンソール(Chrome)
  • aws s3 cp
  • aws s3 sync

の3パターンと、比較対象としてWindows10からも同じ3パターンでファイルをS3へアップロードし、そのファイル名をマネージメントコンソール・AWS CLI・Pythonのlist_objects_v2()から確認します。

アップロードファイル
[Mac]
# マネージメントコンソールからアップロード(Chrome)
mctrl_ばびぶべぼぱぴぷぺぽバビブベボパピプペポ.txt
# aws s3 cp
s3cp_ばびぶべぼぱぴぷぺぽバビブベボパピプペポ.txt
# aws s3 sync
s3sync_ばびぶべぼぱぴぷぺぽバビブベボパピプペポ.txt

[Windows10]
# マネージメントコンソールからアップロード(Chrome)
mctrl-win_ばびぶべぼぱぴぷぺぽバビブベボパピプペポ.txt
# aws s3 cp
s3cp-win_ばびぶべぼぱぴぷぺぽバビブベボパピプペポ.txt
# aws s3 sync
s3sync-win_ばびぶべぼぱぴぷぺぽバビブベボパピプペポ.txt

マネージメントコンソールから確認した場合は以下のようになります。

一見すると何の問題も無いように見えます。

一方、AWS CLIからaws s3 lsで対象のバケットのファイルを見ると以下のようになります。

またPythonのboto3のlist_objects_v2()でバケット内のファイル名を表示すると以下のようになります。

これから分かるように赤い矢印で指し示したMacOSのマネージメントコンソール経由でアップロードしたもの(mctrl_xxxx.txt)とaws s3 syncでアップロードしたもの(s3sync_xxxx.txt)は濁音・半濁音が分離されてしまっていることがわかります。

まとめ

まず不特定多数のクライアントで利用する可能性のあるS3バケットではマルチバイトの利用は避けましょう。 それでもMacOSからマルチバイトのファイル名を扱う必要がある場合は、ファイルをS3へアップロードする方法としてマネージメントコンソールではなく、AWS CLIのs3 cpを使ってS3へファイルをアップロードするように気をつけましょう。

最後まで読んで頂いてありがとうございました。