JavaScript AWS SDKでS3にある画像をブラウザに表示

JavaScriptAWS SDKを使って、S3にある画像ファイルをブラウザから直接取得して表示してみました。

前提

  • S3バケットに画像ファイルが保存されています。
  • S3バケットはパブリックアクセスを拒否しています。IAMユーザが必要です。
  • IAMユーザのアクセスキー、シークレットキーをJavaScriptのソースにハードコーディングしてもよいものとします。
  • ローカルでの検証で、ブラウザ上のURLはhttp://localhost:8080とします。

S3バケットのCORS設定

S3バケットにてCORS(Cross-Origin Resource Sharing)の設定が必要です。この設定がないと、ブラウザのコンソールに以下のようなエラーメッセージが表示され、画像が表示できません。

Access to XMLHttpRequest at 'https://example.s3.ap-northeast-1.amazonaws.com/1.jpg' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

CORSについては以下のAWS公式ドキュメントに説明があります。

AWSマネジメントコンソールからはS3バケットのPermissionsのCORS configurationのところにXMLを書くことで設定できます。

f:id:suzuki-navi:20201015165112p:plain

以下の内容のXMLを書いておきます。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

ソースコード

HTMLファイルとJSファイルの2ファイルです。

$ tree
.
├── index.html
└── lib.js

0 directories, 2 files

AWS SDKをHTMLファイルから読み込んでいます。

Vue.jsも使ってますが、便利だから使ったのみで、ここでは本質的ではありません。

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>S3 Image Viewer Sample</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12" defer></script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.771.0.min.js" defer></script>
<script src="lib.js" defer></script>
</head>
<body>
  <div id="body">
    <s3-image></s3-image>
  </div>
</body>
</html>

lib.js

// リージョンとS3バケット名と画像ファイルのパス
AWS.config.region = "ap-northeast-1";
const s3BucketName = "BUCKETNAME";
const s3Key = "KEY";

// IAMユーザのアクセスキーとシークレットキー
const credentials = new AWS.Credentials();
credentials.accessKeyId = "XXXXXXXXXXXXXXXXXXXX";
credentials.secretAccessKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
AWS.config.credentials = credentials;

const s3ImageData = {
    content: false,
};

const s3 = new AWS.S3();
s3.getObject({
    Bucket: s3BucketName,
    Key: s3Key,
}, function (err, data) {
    if (err) {
        console.log(err);
        return;
    }
    s3ImageData.content = data.Body;
    console.log(data);
});

Vue.component("s3-image", {
    data: function () { return {
        s3ImageData: s3ImageData,
    };},
    computed: {
        imgurl: function () {
            if (s3ImageData.content === false) return "";
            const bytes1 = s3ImageData.content;
            var bytes2 = "";
            for (var i = 0, len = bytes1.byteLength; i < len; i++) {
                bytes2 += String.fromCharCode(bytes1[i]);
            }
            return "data:image/jpeg;base64," + window.btoa(bytes2);
        },
    },
    template: `
        <img v-bind:src="imgurl">
    `,
});

new Vue({ el: "#body" })

実行

以下のようなコマンドで、ローカル検証用の簡易的なHTTPサーバが立ち上がります。

$ python -m http.server 8080

HTTPサーバ起動後にブラウザで http://localhost:8080 にアクセスすると、S3にあった画像が表示されています。エラーがあればブラウザのコンソールにエラーメッセージがあるかもしれません。