Nick Johnson は、これについていくつかの素晴らしいブログ記事を書きました。彼は、 Pluploadと呼ばれる一般的で広く受け入れられている JavaScript アップロード コンポーネントを使用し、Python で記述された AppEngine アプリにファイルをアップロードします。Plupload は、複数のファイル選択 (HTML5、フラッシュ、Silverlight など) をサポートするためのさまざまなバックエンド (ランタイム) をサポートし、アップロードの進行状況やその他のアップロード関連のクライアント側イベントを処理します。
彼のソリューションの問題点は、(1) Python にあり、(2) JavaScript にあることです。ここでgwt-pluploadが登場します。これは、GWT 環境内で Plupload を使用できるようにする、Samuli Järvelä によって作成されたPlupload用の JSNI ラッパーです。ただし、プロジェクトは古くなっています (2010 年以降はコミットされていません) が、インスピレーションとして使用できます。
したがって、複数ファイルのアップロード コンポーネントを構築するための段階的な手順は次のとおりです。これはすべて 1 つのプロジェクトになりますが、(特に JSNI ラッパー) を独自の .jar ファイルまたはライブラリに抽出して、他のプロジェクトで再利用することができます。ソース コードは、こちらの Bitbucket で入手できます。
このアプリケーションは、http://gwt-gaemultiupload-example.appspot.com/ の AppEngine で利用できます (課金対象外なので、利用可能または動作しているとは考えないでください) 。
スクリーンショット

ステップ 1 - サーブレット
ブロブストアは次のように機能します。
- クライアントは、ファイルのアップロードに使用できる URL を blobstore に要求します。
- クライアントは、受信した URL にファイルを POST します。
- POST 全体が受信されると、blobstore はクライアントを成功 URL (アップロード URL の作成時に指定) にリダイレクトします。
これをサポートするには、2 つのサーブレットが必要です。1 つはファイルのアップロード用の URL を生成するため (各ファイルのアップロードには一意の URL が必要であることに注意してください)、もう 1 つは完了したアップロードを受け取るためのものです。どちらも非常に単純です。以下は、URL ジェネレーター サーブレットです。これは、URL をプレーン テキストで HTTP 応答に書き込むだけです。
public class BlobstoreUrlGeneratorServlet extends HttpServlet {
private static BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Content-Type", "text/plain");
resp.getWriter().write(blobstore.createUploadUrl("/uploadfinished"));
}
}
次に、成功したアップロードを受信するためのサーブレットで、ブロブキーをSystem.out
次の場所に出力します。
public class BlobstoreUploadFinishedServlet extends HttpServlet {
private static BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map<String, List<BlobKey>> blobs = blobstore.getUploads(req);
List<BlobKey> blobKeyList = blobs.get("file");
if (blobKeyList.size() == 0)
return;
BlobKey blobKey = blobKeyList.get(0);
System.out.println("File with blobkey " + blobKey.getKeyString() + " was saved in blobstore.");
}
}
これらも に登録する必要がありweb.xml
ます。
<servlet>
<servlet-name>urlGeneratorServlet</servlet-name>
<servlet-class>gaemultiupload.server.BlobstoreUrlGeneratorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>urlGeneratorServlet</servlet-name>
<url-pattern>/generateblobstoreurl</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>uploadFinishedServlet</servlet-name>
<servlet-class>gaemultiupload.server.BlobstoreUploadFinishedServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>uploadFinishedServlet</servlet-name>
<url-pattern>/uploadfinished</url-pattern>
</servlet-mapping>
ここでアプリを実行して にアクセスするhttp://127.0.0.1:8888/generateblobstoreurl
と、次のようなものが表示されます
http://<computername>:8888/_ah/upload/ahpnd3QtZ2FlbXVsdGl1cGxvYWQtZXhhbXBsZXIbCxIVX19CbG9iVXBsb2FkU2Vzc2lvbl9fGAEM
その URL にファイルを投稿すると、ブロブストアに保存されます。ただし、ローカル開発 Web サーバーのデフォルト URL は であるのhttp://127.0.0.1:8888/
に対し、blobstore によって生成される URLは であることに注意してくださいhttp://<computername>:8888/
。これは後で問題を引き起こします。セキュリティ上の理由から、Plupload はファイルを別のドメインに POST できないからです。これはローカル開発サーバーでのみ発生し、公開されたアプリには 1 つの URL しかありません。Eclipse で実行構成を編集して修正-bindAddress <computername>
し、引数に追加します。これにより、ローカル開発サーバーがhttp://<computername>:8888/
代わりに Web アプリをホストします。<computername>
この変更後にアプリをロードするには、GWT ブラウザー プラグインで許可する必要がある場合があります。
これまでのところ、必要なサーブレットは揃っています。
ステップ 2 - Plupload
Plupload をダウンロードし (私は最新バージョンの 1.5.4 を使用しました)、解凍し、js
フォルダーをwar
GWT アプリケーションのディレクトリーにコピーします。この例では、独自の GUI を作成するため、jquery.plupload.queue
orは使用しません。Google APIjquery.ui.plupload
からダウンロードした jQuery も必要です。
次に、アプリケーションに JavaScript を含める必要があるため、以下を編集してタグindex.html
に追加します。<head>
<script type="text/javascript" language="javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" language="javascript" src="js/plupload.full.js"></script>
これで、Plupload がアプリケーションに組み込まれました。次に、GWT で使用できるようにラップする必要があります。ここで gwt-plupload が使用されます。プロジェクトの jar ファイルは使用しませんでしたが、代わりにソース ファイルをコピーして変更できるようにしました。ラッパーの主なオブジェクトは、Plupload
によって構築されるクラスPluploadBuilder
です。PluploadListener
クライアント側のイベントを受け取るために実装できるinterface もあります。
ステップ 3 - 組み立てる
それでは、実際に GWT アプリケーションで Plupload を使用する必要があります。Index.ui.xml
UIBinderに以下を追加しました。
<g:Button text="Browse" ui:field="btnBrowse" />
<g:Button text="Start Upload" ui:field="btnStart" /><br />
<br />
<h:CellTable width="600px" ui:field="tblFiles" />
ファイルを参照するためのボタン、アップロードを開始するためのボタン、およびアップロード ステータスを表示するために使用する CellTable があります。ではIndex.java
、次のように Plupload を初期化します。
btnBrowse.getElement().setId("btn-browse");
PluploadBuilder builder = new PluploadBuilder();
builder.runtime("html5");
builder.useQueryString(false);
builder.multipart(true);
builder.browseButton(btnBrowse.getElement().getId());
builder.listener(this);
plupload = builder.create();
plupload.init();
このruntime
属性は、どのバックエンドを使用するかを Plupload に指示します (私は HTML5 のみをテストしましたが、他のバックエンドも同様に機能するはずです)。Blobstoremultipart
を有効にする必要があります。また、ブラウズ ボタンの ID を設定し、Plupload にその ID を使用するように指示する必要があります。このボタンをクリックすると、Plupload のファイル選択ダイアログが表示されます。最後に、自分自身をリスナー (実装PluploadListener
)create()
およびinit()
Plupload として追加します。
アップロードの準備が整ったファイルを表示するにはtblFilesDataProvider
、 からのイベントでリスト データ プロバイダーにデータを追加するだけですUploadListener
。
@Override
public void onFilesAdded(Plupload p, List<File> files) {
tblFilesDataProvider.getList().addAll(files);
}
進行状況を表示するには、進行状況の変更が通知されるたびにリストを更新するだけです。
@Override
public void onFileUploadProgress(Plupload p, File file) {
tblFilesDataProvider.refresh();
}
btnStart
また、Plupload にアップロードを開始するように指示する のクリック ハンドラーも実装します。
@UiHandler("btnStart")
void btnStart_Click(ClickEvent event) {
plupload.start();
}
ファイルを選択できるようになりました。ファイルは保留中のアップロード リストに追加され、アップロードを開始できます。あとは、先ほど実装したサーブレットを実際に使用するだけです。現在、Plupload は POST アップロード先の URL を認識していないため、それを伝える必要があります。ここで、gwt-plupload ソース コードに変更を加えました (マイナーなバグ修正は別として)。Plupload に という関数を追加しましたfetchNewUploadUrl
。これが行うことは、先ほど定義したサーブレットで Ajax GET リクエストを実行して、アップロード URL をフェッチすることです。これは同期的に行われます (理由は後で明らかになります)。リクエストが返されると、この URL を Plupload の POST URL として設定します。
private native void fetchNewUploadUrl(Plupload pl) /*-{
$wnd.$.ajax({
url: '/generateblobstoreurl',
async: false,
success: function(data) {
pl.settings.url = data;
},
});
}-*/;
public void fetchNewUploadUrl() {
fetchNewUploadUrl(this);
}
Plupload は、各ファイルを独自の POST リクエストで投稿します。これは、各アップロードが開始される前に、新しい URL を提供する必要があることを意味します。幸いなことに、PluploadListener
実装できるイベントがあります。リクエストが同期でなければならない理由は次のとおりです。そうしないと、以下のイベント ハンドラーでアップロード URL を受け取る前にアップロードが開始されます (pl.fetchNewUploadUrl()
すぐに返されます)。
@Override
public void onBeforeUpload(Plupload pl, File cast) {
pl.fetchNewUploadUrl();
}
以上です!AppEngine Blobstore にファイルを配置する GWT HTML5 複数ファイル アップロード機能が追加されました。
パラメータを渡す
追加のパラメーター (アップロードされたファイルが属するエンティティの ID など) が必要な場合は、追加する方法の例を追加しました。次のように実装したPlupload
というメソッドがあります。setExtraValue()
public native void setExtraValue(String value) /*-{
this.settings.multipart_params = {extravalue: value}
}-*/;
追加の値は として渡すことができますmultipart_params
。これはマップであるため、機能を拡張して任意のキーと値のペアを多数許可できます。onBeforeUpload()
値はイベント ハンドラで設定できます
@Override
public void onBeforeUpload(Plupload pl, File cast) {
pl.setExtraValue(System.currentTimeMillis() + " is unique.");
pl.fetchNewUploadUrl();
}
完了したアップロードを受信するサーブレットで次のように取得されます
String value = req.getParameter("extravalue");
サンプル プロジェクトには、このためのサンプル コードが含まれています。
最後の言葉
私は GWT 開発の専門家ではありません。これは、求めていた機能が見つからないことに何時間もフラストレーションを感じた後に思いついたものです。動作するようになった後、使用した/フォローしたすべてのコンポーネント/ブログ投稿/その他で一部が省略されていたため、完全な例を作成する必要があると考えました。これがベスト プラクティス コードであることを意味するものではありません。コメント、改善、提案は大歓迎です!