DWR を使用した Ajax ベースのファイル・アップロード・ポートレットの開発

ファイル・アップロード・プロセスの進行状態がわかるプログレス・バーを表示する

ファイル・アップロードは今日の Web ポータルにとって基本的な機能です。この記事では、Xiaobo Yang と Rob Allan が DWR (Direct Web Remoting) を使って Ajax をベースとした JSR 168 準拠のファイル・アップロード・ポートレットを開発する方法を説明します。DWR はサーバー・サイドにデプロイされた Java クラスに基づいて動的に JavaScript を生成するため、Java™ 開発者にとっては理想的な Ajax フレームワークとなります。この記事を読んで、DWR を利用してポータル・サーバーからファイル・アップロードの進行状態を取得する方法を学んでください。

Xiaobo Yang, Software Developer, STFC e-Science Centre, UK

Xiaobo YangXiaobo Yang は、英国 CCLRC e-Science Centre の Grid Technology Group に所属するソフトウェア開発者です。グリッド、連携仮想調査環境、そしてグリッド・ポータルやサービス指向アーキテクチャー (SOA) をはじめとするさまざまな Web 技術を専門としています。



Robert Allan (r.j.allan@dl.ac.uk), Group Leader, STFC e-Science Centre, UK

Robert AllanRob Allan は、STFC e-Science Centre の Grid Technology Group でリーダーを務めています。最新技術を使用したハイパフォーマンス・コンピューティング・アプリケーションの物理学者兼開発者としての経歴は 1980年代中頃に遡り、英国での大規模な HPC および e-サイエンス・プロジェクトのいくつかを管理した経験もあります。彼の専門分野は、グリッドをより広範に使用できるようにすることです。



2007年 8月 21日

はじめに

Web ポータルは、ユーザーが各種のリソースとサービスにアクセスするための中心的ゲートウェイであると同時に、他のユーザーと共有するリソースを提供するためのプラットフォームにもなります。ユーザーが共有するリソースには、写真からオーディオ・ファイルやビデオ・ファイル、そして調査を目的とした科学データ・セットに至るまで、ありとあらゆるものが考えられます。そのため、ファイル・アップロードは今日の Web ポータルに欠かせない基本機能となっています。

現在、Web ポータルが大々的に依存しているのは Java ポートレット技術です。ファイル・アップロードのプログレス・バーは、さまざまな開発者が Ajax を使用して記述してはいるものの、ポートレットをベースとしたプログレス・バーについてまだ開拓の余地があります。そこで、この記事ではファイル・アップロードのプロセス中にプログレス・バーを表示する Ajax ベースのファイル・アップロード・ポートレットを開発する方法を紹介します。このポートレットは、大規模なオーディオ・ファイル、ビデオ・ファイル、科学ファイルを共有しなければならないユーザーにはとりわけ重宝なものとなります。

この記事を理解するには、Java Servlet と JSP (JavaServer Page) を使用した Web 開発についての知識がなければなりません。また、ポータルおよびポートレット技術の開発について理解していることも不可欠です。しかしその一方で、この記事ではポートレット技術の概要を説明するとともに、その理解を深めるのに役立つ資料も紹介しているので、ポートレット技術の熟練者でなくても、ここで断念しないでください。ます。

developerWorks の Ajax リソース・センター

Ajax Resource Center にアクセスしてください。ここには記事、チュートリアル、ディスカッション・フォーラム、ブログ、ウィキ、イベント、そしてニュースなど、Ajax プログラミング・モデルに関する情報が豊富に用意されており、ワンストップ・ショップになっています。新しい情報もここに記載されます。

記事で説明するファイル・アップロード・ポートレットをテストする前に、JSR 168 準拠のポータル・フレームワーク (IBM® WebSphere® Portal Server、Apache Pluto、eXo プラットフォーム、または Liferay Portal など) を試してみるのも一考です。ちなみにこの記事の開発プロセスでは、Apache Pluto 1.0.1、JDK 5.0 Update 10、および Apache Ant Version 1.6.5 を使用しました。

ポートレットの基本概念

概して、ポートレットは Web コンポーネントとして考えることができます。ポートレットはサーブレットと似ていますが、ポートレットが重点に置いているのはアプリケーションのプレゼンテーション層です。ポートレットの標準出力は HTML フラグメントで、これらのフラグメントは Web ポータルで組み立てることができます。ポートレット自体はポートレット・コンテナーによって管理されます。ポートレットの主要な機能は以下のとおりです。

  • 複数のモード: ポートレットは、さまざまなモードで異なる表示をすることが可能です。ポートレットはビュー・モードの他、例えばユーザーが独自のプリファレンスを設定するための編集モードをサポートします。
  • 複数のウィンドウ状態: ポートレットは最小化、最大化などに対応します。
  • カスタマイズ可能なパラメーター: ポートレットで定義可能なパラメーターは、ユーザーがカスタマイズすることができます。

ポートレットについての詳細は、Java Portlet Specification 1.0 (JSR 168) を参照してください (JSR 168 の後継である JSR 286 は、ポートレット間通信やポートレット・フィルターなどの改善を伴って 2007年の終わりにリリースされる予定です)。この仕様へのリンクは「参考文献」に記載しています。


ファイル・アップロード・ポートレットの準備手順

ファイル・アップロード・ポートレットの土台となるのは、Apache Commons FileUpload パッケージです (この記事では、FileUpload と呼びます)。Apache Commons FileUpload Version 1.1 パッケージではサーブレットでのファイル・アップロードだけでなく、ポートレットでのファイル・アップロードもサポートします。この記事では、Apache Commons FileUpload Version 1.2 を使用します。

基本的に、ファイル・アップロードのプログレス・バーを開発するには以下の 2 つのステップが必要です。

  1. サーバー・サイドでファイル・アップロードの進行状態を取得する
  2. クライアント・サイドでポータル・サーバーからアップロードの進行状態を取得して表示する

サーバー・サイドでファイル・アップロードの進行状態を取得する

FileUpload パッケージは、リスナーによるファイル・アップロードの進行状態の取得をサポートします。このような進行状態リスナーをPortletFileUpload に対して設定するため、ファイル・アップロード・ポートレット (この記事の「ダウンロード」セクションに記載したソース・ファイルでの名前は、uk.ac.dl.esc.gtg.myportlets.fileupload.FileUploadPortlet) の doUpload() メソッドで setProgressListener() メソッドを呼び出します (リスト 1 を参照)。

リスト 1. ファイル・アップロード・パッケージの進行状態リスナーの設定
DiskFileItemFactory factory = new DiskFileItemFactory();
PortletFileUpload pfu = new PortletFileUpload(factory);
pfu.setSizeMax(uploadMaxSize); // Maximum upload size
pfu.setProgressListener(new FileUploadProgressListener());

FileUploadProgressListener (リスト 2 を参照) は、org.apache.commons.fileupload.ProgressListener インターフェースを実装するリスナーです。update() メソッドは FileUpload パッケージによって自動的に呼び出され、転送済みバイト数に関する最新情報を更新します。この記事での実装では、データが 10KB 転送されるごとに進行状態を更新して、進行状態の更新が頻繁に行われすぎないようにしています。現行のファイル・アップロード進行状態を計算するための getFileUploadStatus() メソッドは、クライアントが DWR を使って呼び出すことになります (次のセクションで説明)。

リスト 2. ファイル・アップロード・リスナーによるファイル・アップロードの進行状態取得
package uk.ac.dl.esc.gtg.myportlets.fileupload;

import java.text.NumberFormat;

import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class FileUploadProgressListener implements ProgressListener {
        private static Log log = LogFactory.getLog(FileUploadProgressListener.class);

        private static long bytesTransferred = 0;

        private static long fileSize = -100;

        private long tenKBRead = -1;

        public FileUploadProgressListener() {
        }

        public String getFileUploadStatus() {
                // per looks like 0% - 100%, remove % before submission
                String per = NumberFormat.getPercentInstance().format(
                                (double) bytesTransferred / (double) fileSize);
                return per.substring(0, per.length() - 1);
        }

        public void update(long bytesRead, long contentLength, int items) {
                // update bytesTransferred and fileSize (if required) every 10 KB is
                // read
                long tenKB = bytesRead / 10240;
                if (tenKBRead == tenKB)
                        return;
                tenKBRead = tenKB;

                bytesTransferred = bytesRead;
                if (fileSize != contentLength)
                        fileSize = contentLength;
        }

}

クライアント・サイドでファイル・アップロードの進行状態を取得する

ファイル・アップロードの進行状態に関するサーバーとクライアントとの通信は、Ajax によって行います。このポートレットの例では、DWR (Direct Web Remoting) を使用して Ajax サポートを提供することにしました。ブラウザーの JavaScript がサーバー・サイド Java オブジェクトを操作できるようにする DWR は、Java 開発者が Ajax を Web 開発プロセスに導入するために使うには理想的なフレームワークです。ポートレットで DWR を使うには、以下のステップが必要になります (DWR の構成方法についての詳細は、「参考文献」を参照してください)。

DWR ならではの利点は、クライアントがサーバー・サイド Java オブジェクトと通信するところにあります。

  • WEB-INF/web.xml で DwrServlet を構成する (リスト 3 を参照)。
  • WEB-INF/dwr.xml に、クライアントが通信する 1 つ以上のサーバー・サイド・オブジェクトを定義する。リスト 4 では DWR に FileUploadProgressListener を定義して、この自動生成される JavaScript をクライアントが呼び出せるようにしています。また、クライアントが呼び出せるメソッドは getFileUploadStatus のみです。もう一方の public メソッド、update にはアクセスすることはできません (リスト 2 を参照)。
  • DWR 関連の JavaScript コードを fileupload-view.jsp に組み込む (リスト 5 を参照)。
  • DWR ライブラリーをポートレット・アプリケーションに組み込む。
リスト 3. WEB-INF/web.xml での DwrServlet の構成
<!-- DWR servlet -->
  <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <display-name>DWR Servlet</display-name>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
      <param-name>debug</param-name>
      <param-value>false</param-value>
    </init-param>
  </servlet>

<!-- DWR servlet mapping -->
  <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-patter>
  </servlet-mappin>
リスト 4. WEB-INF/dwr.xml
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
                     "http://getahead.org/dwr//dwr20.dtd">

<dwr>
  <allow>
    <create creator="new" javascript="FileUploadProgressListener">
      <param name="class"
             value="uk.ac.dl.esc.gtg.myportlets.fileupload.FileUploadProgressListener"/>
      <include method="getFileUploadStatus"/>
    </create>
  </allow>
</dwr>

リスト 5 に示す JSP ファイル、fileupload-view.jsp を見ると、DWR によってサーバー・サイドからファイル・アップロードの進行状態を取得できる仕組みがわかります。まず、ファイルが選択されて Upload ボタンがクリックされると (図 1を参照)、その 1 秒後に fileupload_ajax_query_upload_status() メソッドが呼び出されます。このメソッドは非同期モードで FileUploadProgressListenergetFileUploadStatus() メソッド (リスト 2 を参照) を呼び出します。DWR の利点はここにあります。つまり、クライアントがサーバー・サイド Java オブジェクトと通信するという点です。応答が受信されると fileupload_ajax_show_upload_status() メソッドが呼び出されて進行状態が更新されます。ファイル・アップロードが完了していなければ、更新された進行状態が 2 秒後に検索されます。

リスト 5. ファイル・アップロード・ポートレットの JSP ファイル、fileupload-view.jsp
<%@ page session="false" %>
<%@ page contentType="text/html" %>
<%@ page import="javax.portlet.PortletURL" %>
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>

<script type="text/javascript"
        src='<%= renderResponse.encodeURL(renderRequest.getContextPath()
                                + "/dwr/interface/FileUploadProgressListener.js") %>'> 
</script>

<script type="text/javascript"
        src='<%= renderResponse.encodeURL(renderRequest.getContextPath()
                                + "/dwr/engine.js") %>'> 
</script>

<script type="text/javascript"
        src='<%= renderResponse.encodeURL(renderRequest.getContextPath()
                                + "/dwr/util.js") %>'> 
</script>

<script type="text/javascript">
  function fileupload_ajax_query_upload_status() {
    FileUploadProgressListener.getFileUploadStatus (fileupload_ajax_show_upload_status);
    return true;
  }

  function fileupload_ajax_show_upload_status(status) {
    if (status == "100")
      document.getElementById("fileupload_progress").innerHTML
                                       ="File successfully uploaded";
    else {
      document.getElementById("progressBar").style.display = "block";
      document.getElementById("fileupload_progress").innerHTML=
                                       "Uploading file: " + status
                                       + "% completed, please wait...";
      document.getElementById("progressBarBoxContent").style.width =
                                       parseInt(status * 3.5) + "px";
      setTimeout(fileupload_ajax_query_upload_status, 2000);
    }

    return true;
  }
</script>

<style type="text/css">
  #progressBar {padding-top: 5px;}
  #progressBarBox {width: 350px; height: 20px; border: 1px insert; background: #eee;}
  #progressBarBoxContent {width: 0; height: 20px; border-right: 1px solid #444;
                                       background: #9ACB34;}
</style>

<h4>File Upload</h4>

<!-- the upload form -->
<% PortletURL pUrl = renderResponse.createActionURL();
 %>
<form action="<%= pUrl.toString() %>"
         enctype="multipart/form-data" method="post"
         onsubmit="setTimeout('fileupload_ajax_query_upload_status()', 1000)">
         
  <input type="file" name="fileupload_upload" value="Upload File">
  <input type="submit" value="Upload">
</form>

<%-- file upload progress bar --%>
<div id="fileupload_progress"></div>
<div id="progressBar" style="display: none;">
  <div id="progressBarBoxContent"></div>
</div>

Apache Pluto によるファイル・アップロード・ポートレットのデプロイメントとテスト

プロセスの次のステップは、Apache Pluto 1.0.1 でファイル・アップロード・ポートレットをデプロイし、テストすることです (注: この記事ではバイナリー・バージョンを使用しました)。

コードをコンパイルしてデプロイする

ポートレットをコンパイルしてデプロイメント用の .war ファイルを作成できるように、この記事からダウンロードできるポートレット・ソース・コードには Ant スクリプトが含まれています。まず以下のライブラリー・ファイルを、ソース・コードのルートの下にある lib ディレクトリーにコピーしてください。

  • commons-fileupload-1.2/commons-fileupload-1.2.jar
  • commons-io-1.3/commons-io-1.3.jar
  • commons-logging-1.0.4/commons-logging-1.0.4.jar
  • dwr-2.0.1/dwr.jar
  • portletapi-1.0/portlet.jar
  • servletapi-2.4/servletapi-2.4.jar

これで、ant buildant war を実行すれば、コードがコンパイルされ、デプロイメント用 .war ファイルが作成されます。すべてが正常に完了すると、dist ディレクトリーの下に myportlets-fileupload.war が作成されているはずです。以下の手順に従って、Apache Pluto 1.0.1 でポートレットをデプロイしてください。

  1. Apache Tomcat を起動して http://localhost:8080/pluto/portal にアクセスします。
  2. Pluto 画面の左側にある Admin リンクをクリックしてポートレットをデプロイします。
  3. myportlets-fileupload.war を指定して Submit をクリックします。
  4. ポートレットのタイトル、説明、レイアウトを定義してから Submit をクリックします。
  5. 表示されるページでもう一度 Submit をクリックします。

ここで、Tomcat を再起動するか、Hot deploy myportlets-fileupload portlet application リンクをクリックするよう求められるので、Hot deploy myportlets-fileupload portlet application リンクをクリックしてください。すると、図 1 に示すようにポートレットがロードされます。

図 1. Apache Pluto で動作するファイル・アップロード・ポートレット
Apache Pluto で動作するファイル・アップロード・ポートレット

ファイル・アップロード・ポートレットをテストする

ポートレットがデプロイされたら、今度はファイルをアップロードします。プログレス・バーを表示するには、ポータル・サーバー以外のコンピューターからポートレットにアクセスしなければなりません。ファイルをアップロードする手順は以下のとおりです。

  1. Browse... ボタンをクリックして、アップロードするファイルを選択します。
  2. Upload ボタンをクリックして、選択したファイルをアップロードします。ファイルがアップロードされている間、プログレス・バーが表示され、更新されます (図 2 を参照)。

Pluto がインストールされているコンピューターからポートレットをテストした場合、プログレス・バーは表示されません。これは、アップロードの最大サイズが 20MB に設定されているためです。アップロードのサイズを変更するには、WEB-INF/portlet.xml ファイルにある fileupload_upload_maxsize を変更してください。

図 2. ファイル・アップロード・ポートレット内でアップロード中のファイル
ファイル・アップロード・ポートレット内でアップロード中のファイル

このポートレットでは、アップロードされたファイルは java.io.tmpdir の下 ($PLUTO_HOME または $CATALINA_HOME の下の temp など) にディスク・ファイルとして保存されます。実際の Web アプリケーションでは、さらに処理が必要になる場合があることに注意してください。例えば、アップロードしたファイルがイメージ・ファイルの場合には Web ブラウザーに表示できるようにするなど、アップロードアップしたファイルはその後の用途に応じてデータベースに保存することもできます。

この手法を適用できるその他のアプリケーション

以上で紹介した手法は、ファイル転送ポートレットにも正常に適用できました。このポートレットは GridFTP プロトコルを利用して、サード・パーティーの 2 つのデータ・グリッド・ノード間で大規模なデータ・セットを管理するというものです。

まとめ

この記事では Ajax を使用して、プログレス・バーを表示するファイル・アップロード・ポートレットを開発する方法を紹介しました。サーバー・サイドでは進行状態リスナーによってファイル・アップロードの進行状態を取得し、クライアント・サイドでは DWR を使ってポータル・サーバーからファイル・アップロードの進行状態を取得して、ユーザーにプログレス・バーを表示するという仕組みを理解していただけたはずです。このポートレットは特に、オーディオ・ファイルやビデオ・ファイル、そして科学データのような大規模なデータ・セットを共有する際に役立ちます。また、この記事の例では、DWR を使用すれば簡単に JSR 168 ポートレットを Ajax 対応にできることも実演しました。


ダウンロード

内容ファイル名サイズ
Source code of the file upload portletwa-aj-dwr.zip13KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, XML
ArticleID=258252
ArticleTitle=DWR を使用した Ajax ベースのファイル・アップロード・ポートレットの開発
publish-date=08212007