2016年12月23日金曜日

ビューアで表示する

変換したデータをFORGEビューアで表示してみます。今まではDHCを使って直接RESTを送受信していましたが、ビューアですからいよいよWebページが必要です。うーむ、これは未知の世界だ。Visual Studioでやるんだから当然.ASP.NETですね。

Visual Studio でFORGEを攻略


(1) Visual Studio を起動し[新しいプロジェクト]を選択
(2) 新しいプロジェクトダイアログボックスで[Web]-[ASP.NET Webアプリケーション]を選択。プロジェクト名は「TryForgeViewer」としてみました。
やっぱ、ASP.NETでしょう
(3) 新しいASP.NETプロジェクトでは、ノーマル?にWeb Formを選びました。Web Formには様々な異論があるようですが、詳細を知らないことをいいことにこれを選択します。だって、デスクトッププログラマとしては慣れたイベントドリブン方式が便利ですからね。

もう、断然Web Formですよねー。
(4) [プロジェクト]-[新しい項目の追加]で「HTMLページ」を選択し、名前(私はForgeViewer.html)をつけて[追加]します。
HTMLページを追加
さあ、準備はできました。

Basic Viewerチュートリアルをフォローしてみる

ビューアを使用するチュートリアルがありますので、これをフォローしてみます。

始める前に!

まず、表示したいデータのurnを取得します。これは前回表示用にSVF形式に変換しましたが、このとき返されたURNを使用します。
URNは変換時のものを使う

またアクセストークンを「data:read」で取得する必要があります。

Step1:HTMLファイルを準備しよう!


次の内容を先ほど準備したHTMLファイルにコピーしてください


<head>
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
    <meta charset="utf-8">

    <!-- The Viewer CSS -->
    <link rel="stylesheet" href="https://developer.api.autodesk.com/viewingservice/v1/viewers/style.min.css" type="text/css">

    <!-- Developer CSS -->
    <style>
        body {
            margin: 0;
        }
        #MyViewerDiv {
            width: 100%;
            height: 100%;
            margin: 0;
            background-color: #F0F8FF;
        }
    </style>
</head>
<body>

    <!-- The Viewer will be instantiated here -->
    <div id="MyViewerDiv"></div>

    <!-- The Viewer JS -->
    <script src="https://developer.api.autodesk.com/viewingservice/v1/viewers/three.min.js"></script>
    <script src="https://developer.api.autodesk.com/viewingservice/v1/viewers/viewer3D.min.js"></script>

    <!-- Developer JS -->
    <script>
        var viewer;
        var options = {
            env: 'AutodeskProduction',
            accessToken: '<YOUR_APPLICATION_TOKEN>'
        };
        var documentId = 'urn:<YOUR_URN_ID>';
        Autodesk.Viewing.Initializer(options, function onInitialized(){
            Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
        });

        /**
        * Autodesk.Viewing.Document.load() success callback.
        * Proceeds with model initialization.
        */
        function onDocumentLoadSuccess(doc) {
            // A document contains references to 3D and 2D viewables.
            var viewables = Autodesk.Viewing.Document.getSubItemsWithProperties(doc.getRootItem(), {'type':'geometry'}, true);
            if (viewables.length === 0) {
                console.error('Document contains no viewables.');
                return;
            }
            // Choose any of the avialble viewables
            var initialViewable = viewables[0];
            var svfUrl = doc.getViewablePath(initialViewable);
            var modelOptions = {
                sharedPropertyDbPath: doc.getPropertyDbPath()
            };
            var viewerDiv = document.getElementById('MyViewerDiv');
            viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv);
            viewer.start(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
        }

        /**
         * Autodesk.Viewing.Document.load() failuire callback.
         */
        function onDocumentLoadFailure(viewerErrorCode) {
            console.error('onDocumentLoadFailure() - errorCode:' + viewerErrorCode);
        }

        /**
         * viewer.loadModel() success callback.
         * Invoked after the model's SVF has been initially loaded.
         * It may trigger before any geometry has been downloaded and displayed on-screen.
         */
        function onLoadModelSuccess(model) {
            console.log('onLoadModelSuccess()!');
            console.log('Validate model loaded: ' + (viewer.model === model));
            console.log(model);
        }

        /**
         * viewer.loadModel() failure callback.
         * Invoked when there's an error fetching the SVF file.
         */
        function onLoadModelError(viewerErrorCode) {
            console.error('onLoadModelError() - errorCode:' + viewerErrorCode);
        }

    </script>
</body>


追加したHTMLファイルのVisual Studioが作成した既存の内容を削除して、単純にこの文字列を上書きします。いろいろ書いていますが、とりあえず気にしないで、36行目と38行目にある<YOUR_APPLICATION_TOKEN>と<YOUR_URN_ID>をアクセストークンとdata:readで取得したアクセストークンで置き換えます。「<」「>」も含めて置き換えます。
<>を置き換える
この後にError Codes とか Step 2がありますが、いまのところこれは無視!早速グーグルクロームで実行してみましょう。アクセストークンの期限が切れていなければ、もうデータを見ることができるはずです。
見えた―!!!!(^◇^)
おお!こりゃあすごい!なんと安直な!いったいどんな理屈なんでしょうねぇ。ちょっと<script>セクションの中を見てみますか。

スクリプトは何を言っているのか?


JavaScriptは基本的に「上から順番に読み込んで実行する」というお約束なので、<script>セクションを上からチェックしてみます。

        var viewer;
        var options = {
            env: 'AutodeskProduction',
            accessToken: '<YOUR_APPLICATION_TOKEN>'
        };
        var documentId = 'urn:<YOUR_URN_ID>';

これは「viewer」「option」「documentid」の変数を宣言しているだけのようですね。問題は次の一文です。

        Autodesk.Viewing.Initializer(options, function onInitialized(){
            Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
        });

次の行がFORGEビューアの初期化を行っています。このAutodesk.Viewing.Initializerのヘルプを見てみると

Autodesk.Viewing.Initializer (options, callback)


となっています。これは「optionsの条件でFORGEビューアを初期化して、それが終わったらcallbackに定義された関数を呼び出せ!」と言っているのです。

options ~ 初期化条件設定

env      (string)

Development Staging Production の三つから選択するのですが、ふつうはProductionです。一般ユーザーの我々は、一番安定している Productionを選ぶか、何も指定しないのがよいです。チュートリアルで指定しているAutodesk Production はもう過去の遺物なので、この行は削除していいです。

getAccessToken     (function)

アクセストークンには有効時間があります。アクセストークンを非同期的に取得する関数をつくってここに指定しておけば、指定した時間が来ればこの関数を呼び出してアクセストークンを取り直す。この内容はチュートリアルのStep2に書かれているのですが、じゃあどういう関数を書けばいいのか?ということについては何も記述されていない。そんなことは自分で考えなさい、ということですかね。というわけで、今はパス。

accessToken  (string)

アクセストークンを文字列で指定。

language  (string)

言語。日本語はjaなので、ちょっと改造して language: 'ja' をoptionsに加えてみてもいいでしょうが特に記述しなくても勝手に日本語になるみたいです。

callback ~ 初期化に成功したらこの関数を実行しなさい!

チュートリアルの場合はこの引数に

function onInitialized(){
            Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);

という、関数リテラルを指定しています。関数がどこでも定義できてしまうのもjavascriptの良いところ?なんでしょうかねぇ。なんかバラバラしてどこに何があるんだかわかりゃしないよ。

このコールバック関数はさらにAutodesk.Viewing.Documentloadを呼び出しています。

Autodesk.Viewing.Document.load(documentId,
                                                onSuccessCallback,
                                                onErrorCallback,
                                                accessControlProperties)

ビューアにデータをロードする関数です。引数の内容はヘルプの下のほうに書いていますが、大体の内容は以下のようになります。

documentid

これは例のurnを指定します。

onSuccessCallback

成功した時はこれを実行しなさい、という関数。このチュートリアルではonDocumentLoadSuccessという関数。<script>セクションで定義されています。ざっくりいうと、アップロードしたファイルに表示可能なビューが一つ以上ある場合、その中から最初のビューを取り出してそれをビューアに表示しましょう!という内容です。これはStep3で詳しく見てみます。

onErrorCallback

失敗した時はこれを実行しなさい、という関数。このチュートリアルではonDocumentLoadFailureという関数。実際の定義(上の緑の部分)を見てみると、エラー情報をブラウザのコンソールに情報を出力するという簡単な内容になっています。

accessControlProperties

これはよくわかりませんねー。Descriptionにもoptionalとなっているし、チュートリアルでも指定していません。わかり次第追記しますので、今回はパス。



何と意外に簡単にビューアに表示できるではないですか。この調子でもう少しチュートリアルを掘り下げてみれば、主要な関数は理解できそうな気がします。


2016年12月21日水曜日

ビューア用に変換する

アップロードしたファイルの変換

FORGEビューアで見ることができるファイルフォーマットは多数ありますが、それはそのファイルを直接見ているのではなく、ビューア用に変換する必要があります。その拡張子はどうやら「svf」という形式のようで、この変換は

Model Derivative API

で行います。デリバティブってなんか危なっかしい響きがありますが、大丈夫なんですかね。その方法を説明したチュートリアルがあります。

Prepare a File for the Viewer

その前に!(Before you begin)やるべきことがあります。
(1) アクセストークンを「data:write」と「data:read」で取得しましょう。
(2) ファイルをアップロードして(前回やりました) objectIdのURNをメモしておいてください。

だそうです。(1)はもう慣れたと思いますが、ポイントはスコープを二つ使うということです。DHCを起動しましょう。

変換用のアクセストークンの取得

いちいちアクセストークンを適切なスコープで取得することが重要なポイントとなります。ヘルプにはこのように書いてあります。
DHCの設定は次の図のようになりますが、

ポイントはSCOPEのところに

data:write data:read

と半角スペースでつないで二つのスコープを入れることです。
scopeをdata:write data:read の二つを設定


これで、SENDボタンを押すと、いつものようにアクセストークンが返ってきます。
{
"access_token": "LBBv4IgAg4qhe7P1zaRJcff6f3VK",
"token_type": "Bearer",
"expires_in": 86399
}


変換命令を送る(Model Derivative API)

アクセストークンを取得できたら、前回アップロードしたファイルをSVF形式に変換します。この掟はヘルプに書いてあります。まずはRequestですが

Method and URIPOST https://developer.api.autodesk.com/modelderivative/v2/designdata/job
これは単純に送信先にPOSTするということですから、DHCとしては

次にヘッダーです。
AuthorizationyesstringMust be Bearer <token>, where <token> is obtained via OAuth
Content-TypeyesstringMust be application/json
<token>は先ほど取得したアクセストークンを使います。ですから、DHCのヘッダーは
ヘッダーの設定
となります。
問題はBODYセクションですが、なんだかこむつかしいことが書かれています。
inputyesobjectGroup of inputs
input.urnyesstringThe design URN; returned when uploading the file to Forge The URN needs to be `Base64 (URL Safe) encoded`_span>.




outputyesstringGroup of outputs
output.formatsyesarray: objectGroup of requested formats/types. User can request multiple formats.
formats.typeyesstringThe requested output types. Possible values: DWGFBXIFCIGESOBJSTEPSTLSVFthumbnail. For a list of supported types, call the GET formats endpoint.
必ず必要なところだけ取り出してみました。JSON形式で書けばいいのですが、なんだかよくわかりませんねぇ。どうやらinputというセクションの中にurnというセクションを入れ子にして作りなさい、という意味らいいです。
従いまして

{
      "input" : {
                   "urn" : Base64でエンコードされたオブジェクト(アップロードしたファイルのobjectid)のurn
                   },
というように書きます。これはこのサイトを使って、前回取得したurnをエンコードします。
urn:の文字も含んでENCODEボタンで変換する。
謎の文字列が表示される
エンコード後の文字はなんだか意味不明ですが、この文字列でオブジェクト(アップロードしたファイル)を指定します。

またoutput.formatsですが、「どんな形式に変換したいのか?」の情報を記入します。これはいろいろなフォーマットを指定できますが、SVFの場合はさらにどんなビューを変換するのかを指定します。
Viewsyesarray: stringRequired options for SVF type. Possible values: 2d3d
指定できるのは2dと3dだけですが、これらを[ ]でくくって指定できます。従いまして、Bodyの部分は例えば以下のようになります。{  "input" : {    "urn" : ”dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6YmxhY2tzbWl0aGJ1Y2tldDEyMDMvc2FtcGxlcHJvamVjdDAxLnJ2"  },  "output" : {    "formats" : [      {        "type" : "svf",        "views" : ["2d", "3d"]      }    ]  }}

"output" : [{"type":"svf", "view":["2d","3d"]}]の指定がちょっと特徴的ですが、JSON形式なので、]や}がチャンと対になっているように気を付けてください。DHCの画面は次のようになります。
: { [を間違えないように
これでSendを押すと200OKの値が返ってくるはずです。
200OK     (゜-゜)気持ちいい!
変換命令が受けつけられました。




今どんな感じ?

変換が開始されましたが、それには時間がかかります。お仕事の進行状況を問い合わせて、データが準備できたかどうか確認してみましょう。
ヘルプはこちら

こちらは現在のオブジェクトの状態を問い合わせるAPIです。使い方はとても単純で
Method and URIGET https://developer.api.autodesk.com/modelderivative/v2/designdata/:urn/manifest
Authentication Contextapp only
Required OAuth Scopesdata:read
Data FormatJSON
これは単純に、URIの「:urn」の部分を、上記のurnに入れ替えて、data:readのアクセストークンを使ってGETするだけです。以下はDHCの設定です。ヘッダーにはAuthorizationに「Bearer アクセストークン」のいつものパターンですね。
長ーいendpoint

やたらと長いendpointになりますねー。この例では
developer.api.autodesk.com/modelderivative/v2/designdata/dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6YmxhY2tzbWl0aGJ1Y2tldDEyMDMvc2FtcGxlcHJvamVjdDAxLnJ2dA/manifest

となりました。レスポンスは
{
"type": "manifest",
"hasThumbnail": "true",
"status": "success",
"progress": "complete",
"region": "US",
"urn": "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6YmxhY2tzbWl0aGJ1Y2tldDEyMDMvc2FtcGxlcHJvamVjdDAxLnJ2dA",
"version": "1.0",
[
{"name": "sampleproject01.rvt""hasThumbnail": "true""status": "success""progress": "complete",…},
{"status": "success""progress": "complete""outputType": "thumbnail""children":[{"guid": "0576ce39-e54e-369e-da0e-7a306abf2144",…}
]
ですので、progress はcompleteなので、変換はすでに終わったようです。

じゃあ、いよいよビューアで表示してみますか!

2016年12月19日月曜日

バケットにファイルをアップロードする

ファイルのアップロード

引き続き、チュートリアルの後半を実行してみます。

Step 2: Upload a file to a storage location

ファイルのアップロードに必要なHTTPリクエストの項目をこちらで確認します。
Resource Information

Method and URI

  • メソッドは「PUT」
  • URIは

    https://developer.api.autodesk.com/oss/v2/buckets/:bucketKey/objects/:objectName
    となっています。:bucketKeyには取得したバケットの名前(:は必要ありません)を、:ObjectNameにはアップロードするファイルにつける任意の名前をつけます。名前は本当のファイル名でもいいし、GUIDのような名前でも構いませんが、拡張子は変更しないでください。

Required OAuth Scopes

  • data:write または data:create で取得したアクセストークン。data:write の場合は、既存のファイルを上書きすることができます。

Data Format

  • データフォーマットはJSON形式

まずは例によって、アクセストークンを取得します。スコープはdata:writeです。DHCを起動し、以下のように設定して、Sendで応答を待ちます。
DHCでアクセストークンを取得する
すると、応答が返ってきます。
アクセストークンを取得した!

ファイルをアップロード

いよいよファイルをアップロードします。ファイルは任意のRevitのファイル(標準添付のサンプルプロジェクトなどを利用してください。)を使用します。

ヘッダーの必要事項はこちらで確認します。
ヘッダーには何が必要か?
たくさん項目がありますが、必ず必要なのはAuthorizationとContent-Lengthだけです。

Authorization


  • Bearer アクセストークン (Bearer+半角スペース+アクセストークン)

Content-Length


  • DHCも含めて、ほとんどのクライアントはファイルを指定すれば、この項目は自動的に設定されます。

DHCの設定は下の図のようになります。
DHC
① BODYはファイルなので「file」を選ぶ
② Revitのファイルを選択します。
③ Bearer アクセストークン
④ developer.api.autodesk.com/oss/v2/buckets/blacksmithbucket1203/objects/sampleproject01.rvt
バケット名とオブジェクトの名前(②のファイル名と一致しなくてもいいが、拡張子は同じにします。)

ファイルのアップロードの設定は、なにか拍子抜けするほど簡単ですねぇ。ほんとにこれでいいんでしょうか?とりあえず[Send]を押してみます。

しばらくすると・・・・・

おお!ちゃんんとアップロードできたじゃないですか!応答の内容をチェックしてみましょう。
アップロード成功!
{
"bucketKey": "blacksmithbucket1203",
"objectId": "urn:adsk.objects:os.object:blacksmithbucket1203/sampleproject01.rvt",
"objectKey": "sampleproject01.rvt",
"sha1": "55d89511380aeaa070be7072ed7a57d89172e5f3",
"size": 17215488,
"contentType": "application/octet-stream",
}

bucketkey

バケットの名前。アップロード時に指定したものと同じ名前が返されます。

objectId

これがアップロードしたファイルのバケット内でのURN(ID)になります。以降、この名前でファイルをs操作します。

ObjectKey

アップロードしたファイルの名前。ファイルはアップロード後は「オブジェクト」と呼ばれます。

うまくアップロードできました。今度はこのファイルをビューア用に「変換」します。作成したバケットはtransient、急がなければ。なぜなら作成したバケットはtransient、つまり24時間しかデータを保持してくれないのだから・・・・



2016年12月8日木曜日

バケットの作成

バケットって?

バケットとは、FORGEが提供している、アナタ専用のストレージです。今回はこのバケットを作成してみます。手順は

  1. バケット作成用のアクセストークンを取得する。
  2. バケットを作成する。

の二段階です。この手順ですが、チュートリアルが提供されているので、これを実行してみましょう。

Create an App-Managed Bucket and Upload a File
アプリ用バケットを作成して、ファイルをアップロードする


というまさに今回の目的にピッタリのチュートリアルです。ただし、Before You Begin (始める前に)のところを見てみると、

Make sure that you have registered an app and successfully acquired an OAuth token with scopes bucket:create, bucket:read, and data:write. This will allow you to create a bucket, get bucket details, and upload a file.

アプリケーションを登録し、アクセストークンを「bucket:create」、「bucket:read」、「data:write.」の各スコープを使って取得してください。これらのスコープを使って得たアクセストークンは、「バケットを作る」、「バケットの詳細情報を得る」、「バケットにファイルをアップロードする」ためのトークンとなります。

などとかいています。ということは、これからバケットを新たに作成するのですから

スコープを[bucket:create]として、アクセストークンを取得


しておく必要があるってことですねー。ではまず、前回の例にならってアクセストークンを手に入れてみます。こちらにHTTPリクエストに必要な項目が書かれています。それによると

Method and URI

POST    https://developer.api.autodesk.com/authentication/v1/authenticate

Headers

Content-Type  application/x-www-form-urlencoded

Body Structure

client_id      前回同様、アプリケーションの登録画面にあります。
client_secret   アプリケーションの登録画面にあります
grant_type    必ず「client_credentials」に設定します。
scope       今回は「bucket:create」です。

必要な情報がわかったところで、DHCを起動して設定します。
DHCに必要事項を設定して[Send]
これで、「200 OK」の返事が来るはずです。
説明を追加
もう、アクセストークンを取得するのは、慣れましたね。

Step1:Create a bucket

では、チュートリアルに戻ります。チュートリアルには

Before uploading a file, create a bucket and set a retention policy using the POST buckets endpoint.

ファイルをアップロードする前にバケットを作成し、ファイルを保持する期間を決めてください。

と書いています。バケットにはアップロードされたファイルを保持する期間があります。マニュアルにはこちらに書いています。

Transient

24時間保持

Temporary

30日間保持。

Persistent

ずっと保持。

この3種類です。チュートリアルではTransientを選択しています。
では作成してみましょう・・・・と言いたいところですが、マニュアルの例はcURLコマンドラインによる解説なので、こちらをみて必要な情報を確認しましょう。

これによると、
今回のData Format はJSON
バケットを作成する場合はリクエスト(要求)、レスポンス(回答)ともにJSON形式です。う(*_*)、JSONって何?というわけでWikiを見てみると・・・・

  • JSON(ジェイソン、JavaScript Object Notation)は軽量なデータ記述言語の1つである。

となっていました。えー?JavaScript ぉー(´・ω・`)。そんなもん知らんぞ!

そして、その書式は

  • {キー:値,キー:値・・・・・・}

です。なんだ、簡単じゃん。文字列の書式のことね。これならいけそう。
さらに、だんだんわかってきたのですが、HTTPリクエストには「ヘッダー」と「ボディ」があり、これはDHCの以下の部分に相当するようです。
DHCの画面もヘッダーとボディに分かれている

では、DHCでリクエストの送信先などを設定しましょう。

リクエストの送信先(endpointというらしい)

https://developer.api.autodesk.com/oss/v2/buckets

メソッド

POST

そしてBODYの書式がJSONなので、BOSYの右端の▼をおしてtextを選択します。残念ながらJSONという選択肢はありません。ですが上記したように簡単な書式なのでtextで十分です。
BODYの書式をtextにする

ヘッダーの設定

ヘッダーは次のように指定されています。
ヘッダーに設定する情報

Authorization

Bearer アクセストークン

Content-Type

必ず「application/json

x-ads-region

バケットの保存先。USとEMEAが選択できます。USはアメリカで、EMEAはヨーロッパ、中東、アフリカを示すようです。おそらく物理的なサーバーの位置を言ってるのだとは思いますが、詳細はよくわかりません。必須ではないので、今回は指定しません。

DHCのヘッダーの設定は下の図のようになります。
Bearer(半角スペース)アクセストークン
ボディの設定
ボディ部分で必ず必要な項目は二つだけです。

bucketKey バケットの名前

A unique name you assign to a bucket. It must be globally unique across all applications and
regions, otherwise the call will fail. Possible values: -_.a-z0-9 (between 3-128 characters in
length). Note that you cannot change a bucket key.

グローバルにユニーク、つまり世界でただ一つの名前である必要があります。文字は3以上128以下の文字数で、小文字のアルファベット、数字、-と_と.が使えるそうです。

policyKey

これが上で説明した、ファイルの保持期間で「transient, temporary, persistent」の三つをえらぶことができます。

それではDHCのBODY部分を設定します。文字は""で囲むようにします。
BODYの記述 2行目の末尾に,が必要
バケットの名前は世界でただ一つだけなければならないので特徴的な名前を付ける必要がありますね。それでは[Send]を押してみます。

バケットづくり成功!

反応が返ってきて[200 OK]と出れば成功です。
200 OK バケット作成成功
返ってきたデータをみると以下のようになっています。

{
  "bucketKey": "blacksmithbucket1203",
  "bucketOwner": "n3bCMAZLQ6bRw7t72nNeG4qEsvO7PGYR",
  "createdDate": 1480727832782,
  "permissions":[
      {
         "authId": "n3bCMAZLQ6bRw7t72nNeG4qEsvO7PGYR",
         "access": "full"
      }
   ],
   "policyKey": "transient"
}

無事バケットを作成することができました。ここで、もう一度Sendボタンを押してみます。すると
エラーメッセージ
今度はエラーになりました。reasonのところに「この名前のバケットはすでに存在しています。」というメッセージが出ました。同じ名前のバケットを複数作ることはできないということですね。なにせ世界で唯一の名前でなければならないので、慎重に名前を設定する必要があります。

つぎに、BODYの名前を「Blacksmithbucket1203」など、大文字のアルファベットを入れて、再度[Send]を押してみます。
エラーメッセージ
今度は、400 Bad Request と返されて、reasonに「使える文字は-_.アルファベット小文字と数字で3以上128文字以下」という、命名規則の制限事項が喚起されています。

なかなか、親切じゃないですか!

次回は、このバケットにデータをアップロードしてみます。