利用 DWR 开发基于 Ajax 的文件上载 portlet

在文件上载过程中显示方便的进度条

developerWorks
文档选项
<script type="text/javascript" language="JavaScript"> </script>
将此页作为电子邮件发送

将此页作为电子邮件发送

讨论

样例代码



级别: 中级

Xiaobo Yang (x.yang@dl.ac.uk), 软件开发人员, STFC e-Science Centre, UK
Robert Allan (r.j.allan@dl.ac.uk), 小组负责人, STFC e-Science Centre, UK

2007 年 9 月 04 日

文 件上载是当今 Web 门户的一个基本功能。在本篇文章中,作者 Xiaobo Yang 和 Rob Allan 描述了如何利用 DWR(Direct Web Remoting)开发基于 Ajax 的文件上载 portlet(遵从 JSR 168 规范)。DWR 是 Java™ 开发人员理想的 Ajax 框架,可基于服务器端已部署的 Java 类动态生成 JavaScript。您还将了解如何使用 DWR 从门户服务器检索文件上载过程。

简介

Web 门户为用户提供了访问各种资源和服务的中心网关。与此同时,它们还为用户提供了与其他用户进行资源共享的平台。从照片到音频、视频文件再到研究用的科学数据集,用户可以共享任何内容。因此,文件上载是 Web 门户的一种基本的必备功能。

当 今的 Web 门户在很大程度上依赖于 Java portlet 技术。虽然很多使用 Ajax 的开发人员都给出了各种各样的文件上载进度条解决方案,但我们还没有听说过哪个是基于 portlet 的。本文展示了如何开发基于 Ajax 的文件上载 portlet,此 portlet 能显示文件上载过程的进度条。此 portlet 对于那些想要共享大型音频、视频和科学文件的人士尤其有用。

要跟上本文的进度,您应该对使用 Java Servlets 和 JavaServer Pages (JSPs) 进行 Web 开发十分熟悉。而且,还必须了解门户和 portlet 技术的开发。当然,如果您对 portlet 技术还不怎么精通,也不要现在就放弃本文,因为您会在本文中看到对 portlet 技术的简单介绍,以及一些有用的资源可用来帮助您加快学习的进程。

developerWorks Ajax 资源中心
请访问 Ajax 资源中心,这里几乎囊括了关于 Ajax 编程模型的所有信息,包括各种文章和教程、论坛、博客、wikis、活动和新闻。

在 测试本文给出的这个文件上载 portlet 前,可以考虑尝试使用一种遵从 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。

portlet 的基本概念

一般地,可以将 portlet 视为一种 Web 组件。Portlet 与 servlet 类似,但前者更关注于应用程序的表示层。portlet 的典型输出是 HTML 片断,这些片断可由 Web 门户随后再组装起来。Portlet 本身由 portlet 容器管理。portlet 的主要特性包括:

  • 多模式:portlet 可以在不同的模式下有不同的视图。例如,除了查看 模式,portlet 还支持编辑 模式以便用户可以设置其自身的首选项。
  • 多个窗口状态:portlet 可以最小化、最大化等。
  • 可定制参数:portlet 可以定义参数,而这些参数可以由用户定制。

要 获得有关 portlet 的更多详细信息,可以参考 Java Portlet Specification 1.0,JSR 168(JSR 168 的后续版本 JSR 286 预计会于 2007 年后半年发布,其中包含了一些改进,比如 portlet 间通信和 portlet 过滤器)。相关链接,请参看 参考资料

开始创建文件上载 portlet

文 件上载 portlet 的基石是 Apache Commons FileUpload 包(本文也称之为 FileUpload)。除了支持 servlet 内的文件上载外,Apache Commons FileUpload Version 1.1 包还支持 portlet 中的文件上载。本文使用的是 Apache Commons FileUpload 版本 1.2。

基本上,开发文件上载进度条需要两步:

  1. 在服务器端检索文件上载过程
  2. 从门户服务器进行客户端的文件上载检索和显示

文件上载过程的服务器端检索

FileUpload 包支持使用侦听器检索文件上载过程。在文件上载 portlet 的 doUpload() 方法(称为 uk.ac.dl.esc.gtg.myportlets.fileupload.FileUploadPortlet,包括在本文 下载 部分所提供的源文件中),通过调用 setProgressListener() 方法为 PortletFileUpload 设置过程侦听器,如 清单 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 实现的。我们选用 Direct Web Remoting (DWR) 来提供 portlet 中的 Ajax 支持。DWR 是一种面向 Java 开发人员的理想框架,可用来将 Ajax 引入 Web 开发过程中,原因是它可以让浏览器中的 JavaScript 与服务器端的 Java 对象互动。要在 portlet 中使用 DWR,必须执行以下步骤 (更多有关如何配置 DWR 的信息,请参看 参考资料):

Direct Web Remoting 的妙处就在于此:客户机可以和服务器端的 Java 对象交互。
  • 通过 WEB-INF/web.xml 配置 DwrServlet(参见 清单 3)。
  • 在 WEB-INF/dwr.xml 内定义一个或更多的客户机可与之通信的服务器端对象。在 清单 4 中,FileUploadProgressListener 针对 DWR 定义以便客户机可以调用这个自动生成的 JavaScript。此外,只有 getFileUploadStatus 方法可以被客户机调用,另一个公共方法 update 则不允许被访问(请参看 清单 2)。
  • 将与 DWR 有关的 JavaScript 代码包括在 fileupload-view.jsp(参看 清单 5)。
  • 将 DWR 库包括在 portlet 应用程序。

清单 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),fileupload_ajax_query_upload_status() 方法会被即刻调用。此方法之后会以异步模式调用 FileUploadProgressListenergetFileUploadStatus() 方法(参见 清单 2)。DWR 的妙处就在于此:客户机可以和服务器端的 Java 对象交互。一旦收到响应,fileupload_ajax_show_upload_status() 方法会被调用以刷新此过程。如果文件上载没有完成,更新后的过程就会在两秒种之后检索。


清单 5. 文件上载 portlet 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"
οnsubmit="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 部署和测试文件上载 portlet

此过程的下一步是用 Apache Pluto 1.0.1. 部署和测试文件上载 portlet(注意:本文使用的是二进制版本)。

代码编译和部署

本文附带的可下载的 portlet 源代码同时还提供有 Ant 脚本,以便能够编译 portlet 和构建部署所需要的 .war 文件。首先,必须将如下的二进制文件复制到源代码根目录下的 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 文件。如果一切顺利,myportlets-fileupload.war 就会出现在 dist 目录下。执行如下步骤以利用 Apache Pluto 1.0.1 部署 portlet:

  1. 启动 Apache Tomcat 并访问 http://localhost:8080/pluto/portal
  2. 单击 Pluto 屏幕左侧的 Admin 链接来部署此 portlet。
  3. 找到 myportlets-fileupload.war,然后单击 Submit
  4. 定义 portlet 标题、描述和布局,然后单击 Submit
  5. 再次单击所显示页面上的 Submit

现在,系统会提示您或者重启 Tomcat,或者单击链接 Hot deploy myportlets-fileupload portlet application。我们建议您单击链接 Hot deploy myportlets-fileupload portlet application。之后,此 portlet 会被加载,如 图 1 所示:


图 1. 运行于 Apache Pluto 中的文件上载 portlet
FileUpload portlet

测试文件上载 portlet

一旦部署完此 portlet 之后,就可以上载文件了。要显示出此进度条,应该从计算机而不是从门户服务器访问此 portlet。执行如下步骤上载文件:

  1. 单击 Browse... 按钮选择要上载的文件。
  2. 单击 Upload 按钮上载选中的文件。在文件上载期间,会显示并更新进度条(参见 图 2)。

如果从安装了 Pluto 的计算机测试此 portlet,就不会看到进度条,原因是所设置的上载大小最大为 20MB。通过在 WEB-INF/portlet.xml 文件内更改 fileupload_upload_maxsize 可以改变此上载大小。


图 2. 文件上载 portlet 正在上载文件
文件上载 portlet 正在上载文件

在 我们的 portlet 中,所上载的文件作为磁盘文件保存在 java.io.tmpdir 下 —— 比如,位于 $PLUTO_HOME 或 $CATALINA_HOME 下的 temp。请注意在实际的 Web 应用程序中,可能还会需要进一步的处理。比如,所上载的文件可能需要存储在数据库中以备后用;如果是图像文件,它就有可能显示在 Web 浏览器内。

分享这篇文章……

digg 将本文提交到 Digg
del.icio.us 发布到 del.icio.us
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值