GCSのバケットにオブジェクトをアップロードする方法の説明です。
おしながき
概要.
種類.
アップロードの方法は複数用意されています。
最初の2つはプロジェクト管理者向けの方法であり、アプリケーションからの利用に向いていませんので、ここでは割愛します。残りの5つが一般的にアプリケーションソフトウェアから利用可能な手段です。
名称 | 概要 | 主な用途 |
---|---|---|
コンソール操作 | GCPコンソールで操作します。 ブラウザからGUIで操作できます。 |
GUIで操作したい管理者用。 |
gsutil | gsutilツールを使用してコマンドラインで操作します。 公式ドキュメントはこちら。 |
CUIで操作したい管理者用。 |
クライアントライブラリ | GAE/GCEのアプリケーションでクライアントライブラリを使用して、オブジェクトを生成します。 | サーバでアップロードされたコンテンツに操作を加えたい場合など。 小さいコンテンツのアップロードに向く。 |
JSON API | RESTful APIであるJSON APIを使用して、直接GCSにアップロードします。 | 特定のユーザによる、ブラウザなどのユーザエージェントからのアップロードに向く。 |
XML API | RESTful APIであるXML APIを使用して、直接GCSにアップロードします。 | |
XML API + 署名付きURL | XML APIに署名付きURLを組わせて使用します。 | 一時的に任意のユーザからのアップロードをしたい場合。 |
Blobstore API | GAEのBlobstore APIを使用して、オブジェクトを生成します。 「Blobstore API for Java 8 の概要」を参照。 |
旧APIの互換性保持のため? |
最後のBlobstore APIについては、他の方法で代用できるため、説明しません。公式ドキュメントにも「注: blobデータの保存には、BlobstoreではなくGoogle Cloud Storageの使用をご検討ください。」と注記されています。
クライアントライブラリ.
概要.
GAE/GCEなどに用意されたライブラリを利用して、アプリケーションからGCSオブジェクトを生成します。公式ドキュメントはこちら。
ユーザエージェントがブラウザやAndroidアプリの場合だと、ユーザエージェントとGCSの間にGAE/GCEのアプリケーションが挟まるので、ユーザエージェントの持っているファイルを単にアップロードするだけにとどまらず、このアプリケーションで内容を操作したりできます。
その一方でGAE/GCEの制限を受けます。GAEの場合だと60秒、リクエスト/レスポンスとも最大32MBの制限の影響を受けますので、サイズの大きいファイルの扱いには向きません。
またGAE/GCEアプリケーションにオブジェクトの書き込み権限が設定されるので、GAE/GCEアプリケーションの該当機能をコールさえすれば誰でもアップロードが可能になります。アップロードできるユーザの制限は、GAE/GCEアプリケーション側で実装が必要になります。
ライブラリの準備.
公式ドキュメントに説明のとおりですが、ライブラリを開発環境にインストールする必要があります。Mavenを使用している場合は、pom.xmlに下記を追加します。
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-storage</artifactId>
<version>1.87.0</version>
</dependency>
<dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-storage</artifactId> <version>1.87.0</version> </dependency>
ライブラリのAPIリファレンスはこちら。
肝になるクラスはざっと以下のとおりです。GCSのオブジェクトはBlobクラスが表します。Objectというクラス名は使用済みなので。「Blob」は「Binary
Large OBject」の一般的な略称として使われます。
パッケージ | クラス | 説明 |
---|---|---|
com.google.cloud.storage | Storage (インターフェイス) | ストレージを直接あれこれするメソッドがいます。 |
BucketInfo | バケットのメタデータを表すクラス。 | |
Bucket | バケットそのものを表すクラス。BucketInfoのサブクラスです。 | |
BlobInfo | オブジェクトのメタデータを表すクラス。 | |
Blob | オブジェクトそのものを表すクラス。BlobInfoのサブクラスです。 | |
com.google.cloud | WriteChannel | オブジェクトのバイナリデータの書き込みに使用します。 |
アップロード.
公式ドキュメントに超簡単なサンプルコードがあります。このコードは最初に説明したとおり、アップロードというより「オブジェクトを生成している」といったほうがふさわしいコードになっています。
以下は、ブラウザから送ったファイルをそのままGCSのバケットに書き込む、GAEのサンプルです。
- BlobInfoクラスでアップロードしたいオブジェクトのメタデータを生成します。Builderクラスが用意されているので、ここではそれを使っています。
- BlobInfo.Builder.setContentType()でコンテンツタイプを設定します。これをやっておかないとコンテンツタイプは"application/octet-stream"になってしまいます。GCPコンソールのブラウザでもコンテンツタイプは"application/octet-stream"になり、プレビューもできなくなります。
- com.google.cloud.WriteChannelクラスを使用して、オブジェクトそのもののデータを書き込みます。
import java.io.InputStream; import java.nio.ByteBuffer; import javax.servlet.http.HttpServletRequest; import com.google.cloud.WriteChannel; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; class UploadSample { // Storageクラスのインスタンスを取得 private Storage storageInstance = StorageOptions.getDefaultInstance().getService(); public void uploadObject(HttpServletRequest request) throws IOException { // 生成するオブジェクトのメタデータを用意 String bucket = request.getParameter("bucket"); String object = request.getParameter("object"); BlobInfo.Builder blobinfobuilder = BlobInfo.newBuilder(bucket, object); // コンテンツタイプを指定しないと、"application/octet-stream"になってしまう String contenttype = request.getHeader("content-type"); if ((contenttype != null) && !contenttype.isEmpty()) blobinfobuilder.setContentType(contenttype); // リクエストボディを読み出して、GCSに書き込み try (WriteChannel writer = storageInstance.writer(blobinfobuilder.build())) { InputStream instrm = request.getInputStream(); byte[] readbuff = new byte[1024*1024]; int readbytes = 0; while ((readbytes = instrm.read(readbuff)) > 0) { ByteBuffer bytebuff = ByteBuffer.wrap(readbuff, 0, readbytes); writer.write(bytebuff); } } } }
ブラウザ側はJavaScriptでajaxで送っています。
- <input type="file">のonchangeイベントでユーザが選択したアップロードしたいファイルの情報を保存しておいて、
- "upload"ボタン押下でGAEフロントエンドのURIを作っています。このURIのクエリーにアップロード先バケット名、オブジェクト名を含んでいます。
- POSTメソッドでアップロードすることにしました。対応するGAEフロントエンドはPOSTを処理できるように実装されている前提です。
- コンテンツタイプは"Content-Type"リクエストヘッダで渡します。
- ajaxでアップロードのリクエストを送信します。
<html> <body> <p> <input id="bucket_name" type="text"/><br/> // アップロード先バケット名の指定用 <input id="upload_file" type="file" name="file"/><br/> <input type="button" value="upload" onclick="uploadObject();"/> // ボタン押下でアップロード開始 </p> <script type="text/javascript"> var uploadFiles; $('#upload_file').change(function() { uploadFiles = this.files; // アップロードしたいファイルの情報を保存 }); function uploadObject() { var bucket = $('#bucket_name').val(); var object = encodeURIComponent(uploadFiles[0].name); // アップロードしたいファイルの情報を得る var uri = '/GAEフロントエンドのパス?bucket=' +bucket +'&object=' +object; var opts = { type: 'POST', // GAEフロントエンドはPOSTを処理するメソッドを実装しておく url: uri, data: uploadFiles[0], processData: false, contentType: uploadFiles[0].type, // コンテンツタイプを設定 }; $.ajax(opts) .done(callbackShowAjaxResult) .fail(callbackShowAjaxResult); } function callbackShowAjaxResult(mess) { // 適当に結果を処理 } </script> </body> </html>
注意点.
- GAE経由でアップロードする場合、GAEの60秒/32MB制限を受けます。当然課金対象にもなります。そのためサイズの小さいコンテンツに向いている方法です。
- BlobInfoに自分でコンテンツタイプを設定しておかないと、コンテンツタイプが"application/octet-stream"になってしまいます。
JSON API.
概要.
GAE/GCEなどを経由することなく、直接GCSのバケットにアップロードする方法の1つです。
公式ドキュメントでは「Google Cloud Storage JSON API の概要」で概要が説明されています。ここではユーザエージェントがブラウザである場合について説明します。
リクエストエンドポイント.
RESTful APIなので、URIのリクエストエンドポイントを持ちます。公式ドキュメントの説明どおりですが、下記のフォーマットです。
https://www.googleapis.com/upload/storage/v1/b/バケット名/o
- JSON APIはHTTPSリクエストのみの対応です。HTTPは使用できないとのことです。
- 上記はオブジェクトのアップロード専用のエンドポイントです。他の機能では別のURIになります。
- 上記の「バケット名」のところに、アップロード先のバケット名が入ります。
- 上記の最後の文字は、アルファベット小文字の「オー」です。
実際にリクエストする際は、この後にクエリーでオブジェクト名などのパラメータを付加します。またリクエストヘッダを使って渡すケースもあります。リクエストヘッダとクエリーの詳細は、「HTTP headers and common query string parameters for JSON」にリストされています。
CORS設定.
「クロスオリジン リソース シェアリング(CORS)」ページの「Cloud Storage CORS のサポート」の章の注意書きに説明されているとおりですが、JSON APIでは常にCORSリクエストが許可されます。バケットにCORSの設定をしていても無視されます。
そのため特にJSON APIでは、CORSについて意識する必要はありません。
ただしajaxでアクセスしていて、CORSではなくアクセスの仕方そのものに問題がある場合でも、リクエストエンドポイントはレスポンスにAccess-Control-Allow-Originヘッダを含めてくれません。そのため表向きCORS違反しているように見えてしまうことがあります。レスポンスのステータスコードが400番台なら、CORS以前に、アクセスの仕方に問題があると思われます。
バケットの権限設定.
バケットはデフォルトで非公開であり、プロジェクト管理者しかアクセスできません。管理者でないユーザがアップロードするには、そのユーザにバケットへのアクセス権を与える必要があります。
その手順はXML APIやアップロード/ダウンロードで共通ですので、別ページにまとめました。
アクセストークンの取得.
JSON APIを利用するには、基本的にアクセストークンが必要です。例外は公開オブジェクトに対するアクセスのみで、この場合はアクセストークンも必要ありません。
アクセストークンの取得は、その準備も含めて手順を踏む必要があります。またXML APIとも共通ですし、アップロードに限らずダウンロードなど他の機能でも必要になります。
こちらのページに、アクセストークンの取得方法をまとめてあります。
アップロード.
公式ドキュメントの「オブジェクトのアップロード」にはcurlを使った例しかないので、ブラウザからアップロードするアプリケーションの参考としては不十分です。「JSON API: Options for uploading objects」にも説明があります。JSON APIの概要は「API reference」に、アップロードの詳細は「Objects: insert」のページに書かれています。
やっとリクエストエンドポイントにアクセスします。次のコードは、最もシンプルなファイル本体のみでメタデータを含まない方法で、ブラウザからajaxでアップロードするサンプルです。
- <input type="file">のonchangeイベントでユーザが選択したアップロードしたいファイルの情報を保存しておいて、
- "upload"ボタン押下でリクエストエンドポイントへのURIを作っています。このURIにアップロード先バケット名、オブジェクト名を含んでいます。
- リクエストURIのクエリーで、シンプルなアップロードを示す"uploadType=media"を指定しています。
- アクセストークンは前記「アクセストークンの取得」 で取得したものです。ここではAuthorizationリクエストヘッダを用いて渡しています。
- 「Objects: insert」の説明ではContent-Length/Content-Typeヘッダが必要とのことです。これらはブラウザが勝手につけてしまうので、このサンプルではあえてつけていません。
- リクエストのボディにはFileオブジェクトを渡しているので、コンテンツタイプはブラウザが認識しているファイルのコンテンツタイプになります。formではないので、multipart/form-dataではありません。
<html> <body> <p> <input id="bucket_name" type="text"/><br/> // アップロード先バケット名の指定用 <input id="upload_file" type="file" name="file"/><br/> <input type="button" value="upload" onclick="uploadObjectByJsonApi();"/> // ボタン押下でアップロード開始 </p> <script type="text/javascript"> var uploadFiles; $('#upload_file').change(function() { uploadFiles = this.files; // アップロードしたいファイルの情報を保存 }); function uploadObjectByJsonApi() { // アップロードしたいファイルの情報を得る var bucket = $('#bucket_name').val(); var object = encodeURIComponent(uploadFiles[0].name); // リクエストURIとアクセストークン var path = 'https://www.googleapis.com/upload/storage/v1/b/' +bucket +'/o?uploadType=media&name=' +object; var access_token = '取得したアクセストークン'; // ajaxで送信 var request = new XMLHttpRequest(); request.open('POST', path); request.setRequestHeader('Authorization', 'Bearer ' +access_token); request.send(uploadFiles[0]); // ファイル本体のみを送信 } </script> </body> </html>
レスポンスのステータスコードに200が返ってくれば、アップロード成功しています。
このサンプルではアクセストークンはAuthorizationリクエストヘッダを用いて渡しましたが、クエリーでaccess_tokenパラメータとして渡すこともできます。その他のリクエストヘッダとクエリーの仕様については「HTTP headers and common query string parameters for JSON」に説明があります。
XML API.
概要.
XML APIは、JSON APIと同等の機能を持っているので、GAE/GCEなどを経由せずに、直接GCSのバケットにアップロードできます。
公式ドキュメントでは「XML API overview」で概要が説明されています。ここではユーザエージェントがブラウザである場合について説明します。
リクエストエンドポイント
RESTful APIなので、URIのリクエストエンドポイントを持ちます。公式ドキュメントの説明どおりですが、JSON APIと違って1つではありません。
- すべての機能を利用可能 (もちろんアップロードも可能)
https://storage.googleapis.com/バケット名/オブジェクト名 https://バケット名.storage.googleapis.com/オブジェクト名
- アップロード専用 (パフォーマンス向上の可能性あり)
https://storage-upload.googleapis.com/バケット名/オブジェクト名 https://バケット名.storage-upload.googleapis.com/オブジェクト名
それぞれがまた2種類あって、1つはパス部分にバケット名を含むもの(上側)、もう1つはホスト名にバケット名を含むもの(下側)です。下側のホスト名にバケット名を含むパターンは、バケットごとに異なるCORSポリシーを設定したい場合に使用します。
実際にリクエストする際は、さらにクエリーやリクエストヘッダでパラメータを渡します。その詳細は「HTTP headers and query string parameters for XML API」にリストされています。
CORS設定.
ajaxを使用すると問題になるCORSについての設定です。JSON APIと違い、XML APIではCORS設定が有効になります。
署名付きURLを使用するケースと同じなので、別ページにまとめました。
バケットの権限設定.
バケットはデフォルトで非公開であり、プロジェクト管理者しかアクセスできません。管理者でないユーザがアップロードするには、そのユーザにバケットへのアクセス権を与える必要があります。
その手順はJSON APIやアップロード/ダウンロードで共通ですので、別ページにまとめました。
アクセストークンの取得.
XML APIを利用する際にも、基本的にアクセストークンが必要です。例外は、JSON API同様に公開オブジェクトに対するアクセスと、次章で説明する署名付きURLを使用する場合です。これら場合はアクセストークンも必要ありません。
アクセストークンの取得については、準備も手順もJSON APIと同じです。またアップロードに限らずダウンロードなど他の機能でも必要になる点も、JSON
APIと同じです。
こちらのページに、アクセストークンの取得方法をまとめてあります。
アップロード.
公式ドキュメントの「オブジェクトのアップロード」には、JSON APIと同じく、curlを使った例しかありません。
XML APIの概要の説明ページ「Overview of request methods for the XML API」にあるとおり、POSTメソッドとPUTメソッドどちらでもオブジェクトのアップロードが可能なようです。POSTメソッドはHTMLのformからのアップロード用、PUTメソッドはform以外でのアップロード用です。アップロードの詳細は、POSTメソッドは「POST Object」に、PUTメソッドは「PUT Object」および「Object upload」のページに書かれています。
ここでは、formを使わずにajaxでアップロードするので、PUTメソッドを使用します。
次のコードは、シンプルにファイル本体のみをformを使わずに、ブラウザからajaxでアップロードするサンプルです。
- <input type="file">のonchangeイベントでユーザが選択したアップロードしたいファイルの情報を保存しておいて、
- "upload"ボタン押下でリクエストエンドポイントへのURIを作っています。このURIにアップロード先バケット名、オブジェクト名を含んでいます。
- リクエストURIにはクエリー、フラグメントともありません。
- アクセストークンは前記「アクセストークンの取得」 で取得したものです。Authorizationリクエストヘッダを用いて渡します。
- 「PUT Object」の説明ではContent-Lengthが必要、Content-Typeヘッダの使用を推奨とのことです。これらはブラウザが勝手につけてしまうので、このサンプルではあえてつけていません。
- リクエストのボディにはFileオブジェクトを渡しているので、コンテンツタイプはブラウザが認識しているファイルのコンテンツタイプになります。
<html> <body> <p> <input id="bucket_name" type="text"/><br/> // アップロード先バケット名の指定用 <input id="upload_file" type="file" name="file"/><br/> <input type="button" value="upload" onclick="uploadObjectByXmlApi();"/> // ボタン押下でアップロード開始 </p> <script type="text/javascript"> var uploadFiles; $('#upload_file').change(function() { uploadFiles = this.files; // アップロードしたいファイルの情報を保存 }); function uploadObjectByXmlApi() { // アップロードしたいファイルの情報を得る var bucket = $('#bucket_name').val(); var object = encodeURIComponent(uploadFiles[0].name); // リクエストURIとアクセストークン var path = 'https://storage.googleapis.com/' +bucket +'/' +object; var access_token = '取得したアクセストークン'; // ajaxで送信 var request = new XMLHttpRequest(); request.open('PUT', path); request.setRequestHeader('Authorization', 'Bearer ' +access_token); request.send(uploadFiles[0]); // ファイル本体のみを送信 } </script> </body> </html>
先に説明したJSON APIを使ったサンプルとそっくりになります。違いは、リクエストエンドポイントと、PUTメソッドを使っていることの、2点だけです。
XML APIでは、アクセストークンをクエリーで渡せません。JSON APIではリクエストヘッダでもクエリーでも、どちらでも渡せました。
「POST Object」の説明のとおり、POSTメソッドを使うとformで送信することになります。この場合はContent-Typeがmultipart/form-dataでないとエラーになります。メソッドとコンテンツタイプの組み合わせがきっちり決まっていて、仕様どおりでない組み合わせはエラーになります。またクエリーも受け取ってくれません。
その代わり、PUTメソッドではクエリーとリクエストヘッダで送信していたパラメータは、formのフィールドで送信するようになっています。success_action_redirectフィールドはページ遷移でアップロードを実装した際に、アップロード成功後のリダイレクト先を指定することができるので、便利そうです。
XML API + 署名付きURL.
概要.
署名付きURLはアクセストークンの代わりに使用します。バケット/オブジェクトへのアクセス権を持たないユーザでも、またユーザがGoogleアカウントでログインしていなくても、アクセスすることができます。署名付きURLには有効期限があり、一時的にアクセス権を与えたい場合に使用します。
概要は公式ドキュメントの「署名付き URL」で説明されています。また「署名付き URL を活用して Cloud Storage に画像ファイルを直接アップロードするアーキテクチャを設計する」にも紹介されています。
署名付きURLの生成については、「独自のプログラムを使用した V4 署名プロセス」に説明があります。この説明を読むと、ややこしいし「手作業かよっ」と感じてしまいます。
しかしクライアントライブラリにメソッドが用意されおり、その使い方は「Cloud Storage のツールを使用した V4 署名プロセス」で説明されています。ただしこのV4署名プロセスはエクスペリメンタルのステートなので、将来仕様変更される可能性も含んでいます。最悪APIそのものが正式版にならない可能性もあります。
そこで以下では、サービスアカウントで署名する方法を説明します。この方法はAPIリファレンスのサンプルコードで説明されています。
手順.
ファイル本体は、ブラウザから直接GCSのバケットにアップロードするものとします。アップロードの手順は、以下のようになります。
- CORS設定を行います。XML APIで説明したのと同じです。
- 準備として、サービスアカウントキーを取得します。
- サービスアカウントキーを使用して、署名付きURLを生成します。GAE/GCEのフロントエンドに生成のAPIを実装すると良いでしょう。
- 生成した署名付きURLを使ってブラウザからアップロードします。
バケット/オブジェクトのへの権限設定も必要ありません。設定してあってもそれらと無関係に一時的な権限を与えるのが、署名付きURLです。
アクセストークンも必要ありません。署名付きURLはアクセストークンと同様に認証情報を持っています。
CORS設定.
署名付きURLを使用する方法もXML APIの範疇なので、CORS設定が反映されます。設定の方法は同じなので、CORS設定のページを参照してください。既にXML APIの章に説明した方法で設定済みなら、そのままで良いはずです。
サービスアカウントキーの取得.
サービスアカウントキーの取得手順は、アップロード/ダウンロードで共通ですので、別ページにまとめました。
署名付きURLの生成.
署名付きURLには有効期限がありますので、アップロードごとに署名付きURLを生成する必要があります。GAE/GCEでクライアントライブラリを使用したサンプルを示します。
- オブジェクトのメタデータを、バケット名/オブジェクト名から生成します。
- さらにメタデータにBlobInfo.Builder.setContentType()メソッドでコンテンツタイプを設定します。
- Storage.signUrl()メソッドで、署名付きURLを生成します。このメソッドはデフォルトでGETメソッドのURLを生成する仕様なので、PUTメソッドを指定しています。またContent-Typeヘッダを署名に含めることを指定しています。
- 生成した署名付きURLは、レスポンスにプレーンテキストとして書き出しています。
import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.HttpMethod; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; class UploadSample { // サービスアカウントキーを格納したファイルのパス private final static String KEYPATH = "WEB-INF/ファイルのパス"; // Storageクラスのインスタンスを取得 private Storage storageInstance = StorageOptions.getDefaultInstance().getService(); public void createSignedUrl(HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException, IOException { // 生成するオブジェクトのメタデータを用意 String bucket = request.getParameter("bucket"); String object = request.getParameter("object"); BlobInfo.Builder blobinfobuilder = BlobInfo.newBuilder(bucket, object); // コンテンツタイプは、アップロード時と一致していること String contenttype = request.getHeader("content-type"); if ((contenttype != null) && !contenttype.isEmpty()) blobinfobuilder.setContentType(contenttype); // 署名付きURLを生成 URL url = storageInstance.signUrl(blobinfobuilder.build(), 30L, TimeUnit.MINUTES, // 有効期間は30分 Storage.SignUrlOption.signWith(ServiceAccountCredentials.fromStream(new FileInputStream(KEYPATH))), // サービスアカウントで署名 Storage.SignUrlOption.httpMethod(HttpMethod.PUT), // PUTメソッドを指定 Storage.SignUrlOption.withContentType()); // Content-Typeヘッダを指定 // 生成した署名付きURLをレスポンスで返す response.getWriter().write(url.toString()); } }
注意すべき点は、下記のとおりです。
- サービスアカウントで署名するためには、Storage.SignUrlOption.withSign()メソッドを使用します。引数のsignerには、「サービスアカウントキーの取得」で取得したキーからServiceAccountCredentials.fromStream()メソッドで生成した署名を指定します。これはStorage.signUrl()メソッドの例のとおりです。
- アップロードの際はPUTメソッドの使用を明示する必要があります。Storage.SignUrlOption.httpMethod()メソッドの説明どおり、指定しなければデフォルトのGETメソッドが使用されます。公式ドキュメントの「GET Object」のとおりXML APIでは、GETメソッドはダウンロードなど取得系のAPIで使用するので、アップロードできなくなります。ここではformを使わずに送信するので、「Object upload」の説明どおり、PUTメソッドを使用します。
- 署名にコンテンツタイプを含めます。コンテンツタイプの値はメタデータにBlobInfo.Builder.setContentType()メソッドで設定しますが、それを署名に含めるにはStorage.SignUrlOption.withContentType()メソッドのコールが必要です。またここで設定したコンテンツタイプは、アップロード時のリクエストのContent-Typeヘッダの値と一致している必要があります。コンテンツタイプが必要な理由は、おそらく「CORS設定」と同じで、Content-Typeヘッダによりプリフライトが行われるためです。
アップロード.
ブラウザから、「署名付きURLの生成」で生成したURLにアップロードします。
以下に、アップロードのサンプルを示します。
- <input type="file">のonchangeイベントでユーザが選択したアップロードしたいファイルの情報を保存しておいて、
- "upload"ボタン押下で署名付きURLを生成するAPIにリクエストします。ここにアップロード先バケット名、オブジェクト名を含んでいます。
- 生成された署名付きURLをそのまま使用して、ファイル本体をアップロードします。
- Content-Length/Content-Typeヘッダはブラウザが勝手につけてしまうので、このサンプルではあえてつけていません。
- リクエストのボディにはFileオブジェクトを渡しているので、コンテンツタイプはブラウザが認識しているファイルのコンテンツタイプになります。
<html> <body> <p> <input id="bucket_name" type="text"/><br/> // アップロード先バケット名の指定用 <input id="upload_file" type="file" name="file"/><br/> <input type="button" value="upload" onclick="getSignedUrl();"/> // ボタン押下でアップロード開始 </p> <script type="text/javascript"> var uploadFiles; $('#upload_file').change(function() { uploadFiles = this.files; // アップロードしたいファイルの情報を保存 }); function getSignedUrl() { // アップロードしたいファイルの情報を得る var bucket = $('#bucket_name').val(); var object = encodeURIComponent(uploadFiles[0].name); // 署名付きURLを取得 $.ajax({ type: 'GET', // GAE/GCEに実装したAPIに合わせる url: GAE/GCEのAPIへのパス, // バケット名とオブジェクト名もここで渡す contentType: uploadFiles[0].type, // 署名のコンテンツタイプ }) .done(function(signedurl) { // URLが得られたらファイル本体のアップロードへ uploadContent(signedurl); }); } function uploadContent(signedurl) { // ajaxでファイル本体を送信 var request = new XMLHttpRequest(); request.open('PUT', signedurl); // 署名付きURL生成時に指定したPUTメソッドで、生成されたURLに request.send(uploadFiles[0]); // ファイル本体のみを送信 } </script> </body> </html>
注意すべき点をまとめておきます。
- 署名付きURLの取得でGETメソッドを使用していますが、GAE/GCEフロントエンドに実装したAPIがGETメソッドで実装されている前提です。
- バケット名/オブジェクト名は生成された署名付きURLに含まれていますので、その生成時に指定するのみです。
- メソッドは署名付きURL生成時にStorage.SignUrlOption.httpMethod()メソッドで指定したメソッドと一致している必要があります。上記のサンプルはシングルパートでアップロードしているのでPUTメソッドを使用しますが、formでPOSTするのなら、署名付きURL生成時にPOSTメソッドを指定する必要があります。
- PUTでアップロードするとブラウザが勝手にContent-Typeヘッダを付加しますが、この値が署名付きURL生成時に指定したものと一致している必要があります。
Copyright 2005-2024, yosshie.