Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

Amazon S3メタデータのサイズ制限に対するエンコーディング戦略

イントロダクション

みなさんはS3のメタデータを使っていますか?

技術本部 Sansan Engineering Unitでソフトウェアエンジニアをしている住江です。冬は毎週末、スノボ・温泉・しゃぶ葉に行っています。

営業DXサービス「Sansan」には「コンタクト」という機能があり、面会や電話などの議事録を残し、商談の管理ができます。月間40万件程度登録される、Sansanの中でも重要な機能となっています。コンタクトにはファイルを添付し、商談で使用されたPDFなどのファイルを共有できます。 しかし、今まではファイルは1つしか添付できず商談で使用した資料が複数ある場合は運用に乗りづらく、複数添付できるようにしたいという要望が月1件ほど恒常的に上がってました。

本記事では、そのファイル添付機能で複数添付できるように改修した際に発生した課題とその解決策について書いていきます。端的に言うと課題は、添付ファイルを利用する他機能と同様にファイル名をS3のメタデータにURLエンコードで変換し格納すると要求仕様を満たせない可能性があったことです。解決策は、UTF-8とBase64を組み合わせて変換することでした。

課題の背景: S3のメタデータに保存できない

コンタクトの添付ファイルは、「コンタクト作成画面で1つずつアップロードして一時保存→コンタクト登録時に永続化」という流れになっています。 従来の1つまで添付できる仕様の場合は、永続化時に添付ファイルのファイル名をリクエストに含めて永続化を行っていました。しかし、複数添付できるようにしようとするとフロントエンドの都合上複数のファイル名を扱いづらく実装が複雑になってしまいます。 そこで、添付ファイルを利用する他機能を参考に、アップロードして一時保存する際にファイル名をバックエンドで保持しておき、永続化時にそれを取得するという流れとしました。こうすることによりフロントエンドで複数のファイル名を扱うことなく実装できます。

ファイルの一時保存にはAmazon S3を用い、ファイルをオブジェクトとして、ファイル名をオブジェクトメタデータとして保存する設計でした。S3のオブジェクトメタデータはUS-ASCIIに収録されている文字のみ格納できるため、他機能の実装に倣いファイル名はUTF-8を用いたURLエンコードで符号化しUS-ASCII文字に変換してから保存する想定でした。 また、ファイル添付機能のファイル名の文字数は最大255文字までという仕様があります。

しかし、日本語文字(ひらがなや漢字)のみで構成された255文字のファイル名の場合、S3に保存できないという問題が発覚しました。

技術的な制約:メタデータの制限とエンコード方式

なぜ保存できなかったのでしょうか?

まず、Amazon S3のオブジェクトメタデータで使用できる文字とサイズ制限について説明します。使用できる文字はUS-ASCII文字です。また、すべてのキーと値の合計サイズは、UTF-8で符号化して2KBまでの制限があります。例えば、サイズの計算は以下のようになります。

キー 合計サイズ
x-amz-meta-filename ABCD 23バイト (キー19バイト + 値4バイト)

次に、日本語文字をURLエンコードで符号化した際の変換について説明します。URLエンコードは各文字コードで符号化された表現に「%」をつけて表現する符号化方式です。日本語文字(Unicodeの基本多言語面に収まる範囲内)1文字をUTF-8によるURLエンコードで変換した際はUS-ASCII文字最大9文字となります。例えば、以下のようになります。

日本語文字 URLエンコードで符号化した表現 UTF-8でのサイズ
%E3%81%82 9バイト

以上より、日本語文字255文字をURLエンコードで変換すると2295(=9×255)バイトとなり、キーのサイズと合わせてメタデータサイズ制限の2KB(=2048バイト)を超えてしまい、保存できなくなってしまいます。

キー 保存したい日本語文字 合計サイズ
x-amz-meta-filename あいう...(255文字) %E3%81%82%E3%81%84%E3%81%86...(2295文字) 2314バイト (キー19バイト + 値2295バイト)

解決策の提案: エンコード方式の変更

この問題は、URLエンコードではなくBase64でエンコーディングすることにより解決しました。Base64でエンコーディングした際もUS-ASCII文字に変換されるためオブジェクトメタデータとして保存できます。また、URLエンコーディングよりも日本語文字の変換後のサイズ効率が良くなります。

Base64はビット列を6ビットずつ分割してそれぞれ1文字のASCII文字にする符号化方式です。日本語文字(Unicodeの基本多言語面に収まる範囲内)1文字をUTF-8でビット列にした後Base64で変換した際はUS-ASCII文字最大4文字となります。例えば、以下のようになります。

日本語文字 UTF-8で符号化したビット列 Base64で符号化した表現 UTF-8でのサイズ
E38182(16進数表現) 44GC 4バイト

よって、同様に日本語文字255文字をUTF-8とBase64で変換すると1020(=4×255)バイトとなり、キーのサイズと合わせてもメタデータサイズ制限の2KB(=2048バイト)を超えず保存できるようになります。

キー 保存したい日本語文字 合計サイズ
x-amz-meta-filename あいうえお...(255文字) 44GC44GE44GG44GI44GK...(1020文字) 1039バイト (キー19バイト + 値1020バイト)

これで、当初問題となっていた日本語文字の保存が可能となりました。しかし、「👨‍👨‍👦‍👦」や「𩸽」などUnicodeの基本多言語面に含まれない文字に関しては、1文字あたり4バイトで変換できるとは限りません。添付ファイルの名前にそのような文字が使用されることは少ないため、仕様を調整し、制限を超えている場合と同様のフィードバックを返すようにしました。

C#での実装例は以下の通りです。

if (input.FileName.Length > 255)
{
    // 文字数を超過しているとフィードバックを返す
}

var request = new PutObjectRequest();
request.Metadata.Add("filename", Convert.ToBase64String(Encoding.UTF8.GetBytes(input.FileName)));

try
{
    await _s3Client.PutObjectAsync(request, cancellationToken);
}
catch (AmazonS3Exception ex) when (ex.ErrorCode == "MetadataTooLarge")
{
    // 文字数を超過しているとフィードバックを返す
}

実際には上記の解決策を選択しましたが、他にも以下のような方法を検討しました。

  • DBにファイル名を永続化する方法
    • 今回のS3への保存は一時的な保存の用途だったため採用しませんでした。S3とDBとデータソースが分かれるとアトミックに管理できないので、不要な複雑性を生んでしまいます。
  • ファイル名を書いたテキストファイルをS3の同階層に保存する
    • 同様にアトミックに管理できないため採用しませんでした。
  • ファイル名の変換をShift-JISでバイト列にしてからBase64で変換する
    • 日本語1文字を2バイトで表現できるためサイズ的には有利です。しかし、「𠮟」など常用漢字にもShift-JISに変換できない文字もあるため非採用としました。
  • ファイル名の変換をUTF-16でバイト列にしてからBase64で変換する
    • Shift-JIS同様、基本的な日本語1文字を2バイトで表現できるためサイズ的には有利です。しかし、ASCII文字も2バイトで表現するため(UTF-8は1バイト)、英数字を含むことが多いファイル名に使用するとかえってサイズが大きくなると判断し非採用としました。

このようにして、仕様の要件を満たした設計ができました。また、ファイル名を上限長としてもメタデータのサイズ上限まで余裕があるため、別のデータを格納したり、上限の文字数を増やしたりできるようになりました。

学んだ教訓

この課題に直面したとき学んだ教訓は、既存の設計を流用できないときは設計を見直す余地がないか再考するため公式ドキュメントに当たるのが大事だということです。本事象で言うと、はじめは既存の設計に影響されて、S3のメタデータにはURLエンコードした値しか保存できないと思い込んでいました。しかし、AWSの公式ドキュメントを読むとUS-ASCII文字であるべきとしか書かれておらず、エンコード方法は指定されていないことに気づき、設計を見直すことができました。既存の実装がある場合はつい参考にしてしまいがちですが、このように一次情報に当たることも時には必要です。

まとめ

本記事では、S3にメタデータを保存する際のサイズ制限と、それを回避するための方法を記しました。 ファイル添付機能で複数添付できるようにした改修は無事リリースでき、社内の営業組織や写真を多く共有するユーザー企業様からも、コンタクトの利便性が上がったと喜びの声が上がっていました! 営業DXサービス「Sansan」では、仕様に沿ってユースケースを想定した設計・実装を比較検討しながら緻密に開発する面白さがあります。また、15年以上続く国内有数のSaaSであるため、扱うデータの規模も大きく常にパフォーマンスを意識して開発する必要があり、他のプロダクトではできない経験が得られます。少しでも興味を持たれた方は、ぜひカジュアル面談でお話ししましょう!

Sansan技術本部ではカジュアル面談を実施しています

Sansan技術本部では中途・新卒の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください

参考

© Sansan, Inc.