S3にデプロイしたVue.jsでpublicに配置したPDFを参照したら文字化けしてた

問題の概要

S3にデプロイしたVue.jsアプリケーションで、publicフォルダに配置したPDFファイルを参照した際に文字化けが発生する問題が起きました。

原因

この問題の主な原因は、S3のContent-Typeが正しく設定されていないことです。PDFファイルがtext/plainなど誤ったMIMEタイプで配信されると、ブラウザが正しく解釈できず文字化けが発生します。

解決方法

1. S3のContent-Typeを修正

S3コンソールまたはAWS CLIを使用して、PDFファイルのメタデータを修正します。

aws s3 cp s3://your-bucket/path/to/file.pdf s3://your-bucket/path/to/file.pdf \
  --metadata-directive REPLACE \
  --content-type application/pdf \
  --acl public-read

2. デプロイ時にContent-Typeを指定

Vue.jsのビルド設定やデプロイスクリプトで、PDFファイルのContent-Typeを明示的に指定します。

// vue.config.js または デプロイスクリプト
const mime = require('mime-types');

// S3アップロード時の設定例
{
  ContentType: mime.lookup('file.pdf') || 'application/pdf'
}

3. CloudFrontを使用している場合

CloudFrontのキャッシュを無効化して、新しいContent-Typeが反映されるようにします。

aws cloudfront create-invalidation \
  --distribution-id YOUR_DISTRIBUTION_ID \
  --paths "/path/to/file.pdf"

予防策

  • ビルドプロセスの改善: デプロイ時に自動的に正しいContent-Typeが設定されるようにスクリプトを作成
  • S3バケットポリシーの確認: 適切なメタデータ設定がされているか定期的に確認
  • テスト環境での検証: 本番デプロイ前に必ずPDFの表示を確認
  • 参考

    PDFの正しいMIMEタイプはapplication/pdfです。その他のファイルタイプについても、適切なContent-Typeを設定することで同様の問題を回避できます。

    AWS CDKでの解決方法

    はい、AWS CDKで実装している場合、cdk.pyでS3バケットのデプロイ設定を見直すことで解決できます。

    BucketDeploymentでContent-Typeを指定

    AWS CDKのs3_deployment.BucketDeploymentを使用している場合、content_typeパラメータを設定することで、ファイルごとに適切なMIMEタイプを指定できます。

    from aws_cdk import (
        aws_s3 as s3,
        aws_s3_deployment as s3_deployment,
        Stack
    )
    
    class MyStack(Stack):
        def __init__(self, scope, id, **kwargs):
            super().__init__(scope, id, **kwargs)
            
            bucket = s3.Bucket(self, "MyBucket",
                website_index_document="index.html",
                public_read_access=True
            )
            
            # デプロイ時にContent-Typeを指定
            s3_deployment.BucketDeployment(self, "DeployWebsite",
                sources=[s3_deployment.Source.asset("./dist")],
                destination_bucket=bucket,
                content_type="application/pdf",  # PDFファイル用
                exclude=["*.html", "*.js", "*.css"],  # PDF以外を除外
            )
            
            # HTML/JS/CSS用の別のデプロイメント
            s3_deployment.BucketDeployment(self, "DeployOtherAssets",
                sources=[s3_deployment.Source.asset("./dist")],
                destination_bucket=bucket,
                exclude=["*.pdf"],  # PDFを除外
            )

    より柔軟な設定方法

    複数のファイルタイプを扱う場合は、cache_controlと組み合わせて設定することもできます。

    s3_deployment.BucketDeployment(self, "DeployWithMetadata",
        sources=[s3_deployment.Source.asset("./dist")],
        destination_bucket=bucket,
        metadata={
            "Content-Type": "application/pdf"
        },
        cache_control=[
            s3_deployment.CacheControl.max_age(Duration.days(365))
        ]
    )

    確認ポイント

  • 既存のデプロイメント設定を確認: cdk.py内のBucketDeploymentcontent_typemetadataが設定されているか
  • 再デプロイの実行: 設定変更後はcdk deployを実行して変更を反映
  • CloudFrontのキャッシュクリア: CloudFrontを使用している場合は、デプロイ後にキャッシュを無効化
  • この方法で、CDKのコードレベルでContent-Typeの問題を解決できます。