概要
Djangoで、ユーザーがアップロードしたCSVファイルのヘッダー名が指定のヘッダー名と一致している場合のみアップロードを成功させるという認証方法を実装していました。しかし、ヘッダー名を一致させても頭にBOMの文字列が入っているのが原因で、失敗してしまいました。本記事ではその原因と解決方法を記載します。
エラーが発生したコードと原因
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
csvfile = request.FILES['file']
decoded_file = csvfile.read().decode('utf-8').splitlines()
reader = csv.reader(decoded_file)
header = next(reader)
if header == ['hoge', 'foo']:
# 省略
上記のコードでCSVをアップロードするとなぜかif
分のところで引っかかってしまいます。
おかしいなぁ、と思ってheader
に何が入っているかデバッグしてみました。
すると、以下のように出力されました。
['\ufeffhoge', 'foo']
ん、\ufeff
ってなんだ?CSV開いても特に頭には何もつけていません...
調べてみると、この\ufeff
っていうのは、バイト順マーク、通称BOM(Byte Order Mark)と呼ばれる特殊な文字列とのこと。テキストデータの先頭に配置され、Unicodeエンコーディングを示すために使用されます。テキストの始まりをプログラムに伝える目印みたいなものです。
\ufeff
は、ユニコード文字U+FEFF
を表し、不可視文字とも呼ばれます。
ということで、解決策はこのBOMを除去することということがわかりました。
BOMについては、Wikipediaにも以下のように記載されています。
プログラムがテキストデータを読み込む時、その先頭の数バイトからそのデータがUnicodeで表現されていること、また符号化形式(エンコーディング)としてどれを使用しているかを判別できるようにしたものである
解決方法
BOMを除去するためには、ファイルを開く際にutf-8-sig
のエンコーディングを使用すればOKとのこと。このエンコーディングであれば、BOMを自動的に検出して削除できます。
decoded_file = csvfile.read().decode('utf-8-sig').splitlines()
上記のように修正したら、BOMが除去され、ヘッダー名も正しく一致されました。
よかったよかった。