1、摘要
Java Servlet 程序提供了一个简单的方式发送HTML文件到客户端浏览器。但是,现在一些站点提供了一些非HTML的存取文件,例如Adobe PDF,Microsoft Word, 和 Microsoft Excel 文件等。这篇文章以使用PDF和WORD为例子讲解怎样从SERVLET发送一个非HTML文件到客户端浏览器。事实上,通过本文,你可以通过MIME类型发送任何的格式的文件。同时,也能了解到通过SERVLET怎样与防火墙进行交互。
通过一个SERVLET在浏览器中打开一个文件,你只需简单的把这个文件加入到SERVLET的输出流中。尽管这么做看起来很简单,但是当你打开一个非HTML文件时你必须需要一些事情,例如二进制文件和多个文件的问题。
你可以通过一下方式得到SERVLET输出流:
ServletOutputStream out = res.getOutputStream();
互联网通过MIME(多用途网络邮件扩展)协议发送多部分,多媒体和二进制文件。在SERVLET的RESPONSE对象中,设置你想打开的文件的MIME类型是非常重要的。
2、MIME类型
客户端浏览器通过MIME类型来识别非HTML文件和决定用什么包容器来呈现这个数据文件。插件能够通过MIME类型来决定用什么方式来打开这些文件,当我们下载这些文件以后,我们通过浏览器就可以浏览这些文件了。
MIME类型很有用,他们允许浏览器通过内置技术处理不同的文件类型。
MIME类型对于一个PDF文件是"application/pdf"。当在SERVLET打开一个PDF文件时,你必须在RESPONSE最前面设置"application/pdf":
// MIME type for pdf doc
res.setContentType( "application/pdf" );
打开一个WORD文档,你应该设置成"application/msword":
// MIME type for MSWord doc
res.setContentType( "application/msword" );
对于EXCEL文档,使用MIME类型是"application/vnd.ms-excel"。如果需要的插件系统没有没有安装,浏览器会出现一个提示框,询问用户是否在当前位置打开还是保存到磁盘上。
3、内容部署
部署内容的HTTP response 头允许servlet指定文件类型的信息。使用这个头说明,你可以制定特定的方式来打开这个文件(不仅仅在浏览器中),而且它不应该自动的显示。你可以设置要保存的文件名。这个文件应该是对话框中显示的文件名。如果你不想指定文件名,你得到的是你的servlet同名的文件名称。
在SERVLET中你可以象下面的代码一样来设置头
res.setHeader("Content-disposition",
"attachment; filename=" +
"Example.pdf" );
// attachment - since we don't want to open
// it in the browser, but
// with Adobe Acrobat, and set the
// default file name to use.
如果你想打开一个WORD文件,应该如下做:
res.setHeader("Content-disposition",
"attachment; filename" +
"Example.doc" );
4、封装
剩下的工作就很简单了。你需要基于你想打开的文件的名字创建一个java.net.URL对象。这个字符串应该完整的指出这个文件的位置。举例说明:
String fileURL =
http://www.adobe.com/aboutadobe/careeropp/pdfs/adobeapp.pdf;
你的url字符串应该象http://www.gr.com/pub/somefile.doc或者
http://www.gr.com/pub/somefile.xls这个样子。
但是你必须确认你所打开的文件与你MIME类型中指定的文件类型一定要一致。
URL url = new URL ( fileURL );
5、防火墙
如果你的浏览器需要通过防火墙,最后的一件事情就是你需要担心的就是你的URL连接了。你需要找出一些你的代理服务器的一些信息,例如主机名称和端口号去与防火墙建立连接。如果你使用JAVA 2,你应该创建一个URL连接对象,设置以下的系统属性:
URLConnection conn = url.openConnection();
// Use the username and password you use to
// connect to the outside world
// if your proxy server requires authentication.
String authentication = "Basic " + new
sun.misc.BASE64Encoder().encode("username:password".getBytes());
System.getProperties().put("proxySet", "true");
System.getProperties().put("proxyHost", PROXY_HOST); // your proxy host
System.getProperties().put("proxyPort", PROXY_PORT); // your proxy port
conn.setRequestProperty("Proxy-Authorization", authentication);
如果你使用JDK 1.1你可以不用建立系统属性。你应该用你的代理服务器信息创建JAVA.NET.URL对象。
url = new URL("http", PROXY_HOST,
Integer.parseInt(PROXY_PORT),
fileURL );
// assumes authentication is not required
6、实现
在开始读取你得文件的时候,你需要从URLConnection (or URL)对象中包含InputStream。
在以下的例子中,你用一个BufferedInputStream来限制这个InputStream。
如果你正在使用这个URLConnection,以下就是这样的代码:
BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
如果你正在使用URL,那就象以下的代码:
BufferedInputStream bis = new BufferedInputStream(url.openStream());
一旦做完以上工作,你只需简单的从输入流写出每一个字节到servlet的输出流中
BufferedOutputStream bos = new
BufferedOutputStream(out);
byte[] buff = new byte[2048];
int bytesRead;
// Simple read/write loop.
while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
最后,在最后的模块中关闭这个流。
这个例子通过使用一个servlet的doPost方法来实现这个功能。
public void doPost(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException
{
ServletOutputStream out =
res.getOutputStream ();
//---------------------------------------------------------------
// Set the output data's mime type
//---------------------------------------------------------------
res.setContentType( "application/pdf" ); // MIME type for pdf doc
//---------------------------------------------------------------
// create an input stream from fileURL
//---------------------------------------------------------------
String fileURL =
"http://www.adobe.com/aboutadobe/careeropp/pdfs/adobeapp.pdf";
//------------------------------------------------------------
// Content-disposition header - don't open in browser and
// set the "Save As..." filename.
// *There is reportedly a bug in IE4.0 which ignores this...
//------------------------------------------------------------
res.setHeader("Content-disposition",
"attachment; filename=" +=
"Example.pdf" );
//-----------------------------------------------------------------
// PROXY_HOST and PROXY_PORT should be your proxy host and port
// that will let you go through the firewall without authentication.
// Otherwise set the system properties and use URLConnection.getInputStream().
//-----------------------------------------------------------------
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
URL url = new URL( "http", PROXY_HOST,
Integer.parseInt(PROXY_PORT), fileURL );
// Use Buffered Stream for reading/writing.
bis = new BufferedInputStream(url.openStream());
bos = new BufferedOutputStream(out);
byte[] buff = new byte[2048];
int bytesRead;
// Simple read/write loop.
while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
} catch(final MalformedURLException e) {
System.out.println ( "MalformedURLException." );
throw e;
} catch(final IOException e) {
System.out.println ( "IOException." );
throw e;
} finally {
if (bis != null)
bis.close();
if (bos != null)
bos.close();
}
}
7、总结
正如你所看到的,在一个servlet中打开一个非HTML是很简单的,甚至在一个防火墙之外。你可以使用同样的代码打开图像文件或者其他类型的多媒体文件,只要正确设置MIME类型。今天,在互联网上更多的信息都变得有效了,并且其中大量的信息是以非HTML类型存储的。相比之下,开发一个servlet来呈现非HTML文档是一件容易,方便的方式来提供给你的用户更多的信息。