使用servlet来下载文件,其原理非常简单,只要得到文件的输入流(或相应字节),然后写输出流即可。现就其中的几个细节问题展开:
1. MIME类型的设置:
Web 浏览器使用 MIME 类型来识别非 HTML 文档,并决定如何显示该文档内的数据。
例如EXCEL文件的 MIME 类型是 "application/vnd.ms-excel "。要用servlet 来打开一个 EXCEL 文档,需要将 response 对象中 header 的 contentType 设置成“application/vnd.ms-excel ”。
response.setContentType(contentType);
2. Content disposition
HTTP response header中的content-disposition 允许 servlet 指定文档表示的信息。使用这种header ,你就可以将文档指定成单独打开(而不是在浏览器中打开),还可以根据用户的操作来显示。
如果用户要保存文档,你还可以为该文档建议一个文件名。这个建议名称会出现在 Save As 对话框的“文件名”栏中。如果没有指定,则对话框中就会出现 servlet 的名字。
servlet 中,将 header 设置成下面这样:
response.setHeader("Content-disposition","attachment;filename="+ "Example.xls" );
response.setHeader("Content-Disposition", "inline; filename="fliename)
点击打开会在ie中打开。
需要说明的有三点:
Ø 中文文件名需要进行iso8859-1转码方可正确显示:
fileName = new String(fileName.getBytes("GBK"),"iso8859-1");
Ø 传递的文件名,需要包含后缀名(如果此文件有后缀名),否则丢失文件的属性,而不能自行选择相关程序打开。
Ø 有下载前询问(是打开文件还是保存到计算机)和通过IE浏览器直接选择相关应用程序插件打开两种方式,前者如上代码所示,后者如下:
response.setHeader("Content-disposition","filename="+ "Example.xls" );
3. 在研究文件的上传及下载过程中,有几点体会
程序的I/O操作往往是性能的瓶颈所在,java io定义了两个基本的抽象类:InputStream和OutputStream,对于不同的数据类型比如磁盘,网络又提供了不同的实现,java.io也提供了一些缓冲流(BufferedStream),使硬盘可以很快的读写一大块的数据, 而Java基本的I/O类一次只能读写一个字节,但缓冲流(BufferedStream)可以一次读写一批数据,,缓冲流(Buffered Stream)大大提高了I/O的性能。所以:
Ø小块小块的读写数据会非常慢,因此,尽量大块的读写数据
Ø使用BufferedInputStream和BufferedOutputStream来批处理数据以提高性能
Ø对象的序列化(serialization)非常影响I/O的性能,尽量少用
1. MIME类型的设置:
Web 浏览器使用 MIME 类型来识别非 HTML 文档,并决定如何显示该文档内的数据。
例如EXCEL文件的 MIME 类型是 "application/vnd.ms-excel "。要用servlet 来打开一个 EXCEL 文档,需要将 response 对象中 header 的 contentType 设置成“application/vnd.ms-excel ”。
response.setContentType(contentType);
2. Content disposition
HTTP response header中的content-disposition 允许 servlet 指定文档表示的信息。使用这种header ,你就可以将文档指定成单独打开(而不是在浏览器中打开),还可以根据用户的操作来显示。
如果用户要保存文档,你还可以为该文档建议一个文件名。这个建议名称会出现在 Save As 对话框的“文件名”栏中。如果没有指定,则对话框中就会出现 servlet 的名字。
servlet 中,将 header 设置成下面这样:
response.setHeader("Content-disposition","attachment;filename="+ "Example.xls" );
response.setHeader("Content-Disposition", "inline; filename="fliename)
点击打开会在ie中打开。
需要说明的有三点:
Ø 中文文件名需要进行iso8859-1转码方可正确显示:
fileName = new String(fileName.getBytes("GBK"),"iso8859-1");
Ø 传递的文件名,需要包含后缀名(如果此文件有后缀名),否则丢失文件的属性,而不能自行选择相关程序打开。
Ø 有下载前询问(是打开文件还是保存到计算机)和通过IE浏览器直接选择相关应用程序插件打开两种方式,前者如上代码所示,后者如下:
response.setHeader("Content-disposition","filename="+ "Example.xls" );
3. 在研究文件的上传及下载过程中,有几点体会
程序的I/O操作往往是性能的瓶颈所在,java io定义了两个基本的抽象类:InputStream和OutputStream,对于不同的数据类型比如磁盘,网络又提供了不同的实现,java.io也提供了一些缓冲流(BufferedStream),使硬盘可以很快的读写一大块的数据, 而Java基本的I/O类一次只能读写一个字节,但缓冲流(BufferedStream)可以一次读写一批数据,,缓冲流(Buffered Stream)大大提高了I/O的性能。所以:
Ø小块小块的读写数据会非常慢,因此,尽量大块的读写数据
Ø使用BufferedInputStream和BufferedOutputStream来批处理数据以提高性能
Ø对象的序列化(serialization)非常影响I/O的性能,尽量少用
例子如下:
File tempFile
=
new
File(
"
d:/temp/output.xls
"
);
WritableWorkbook workbook = Workbook.createWorkbook(tempFile);
WritableSheet sheet = workbook.createSheet( " TestCreateExcel " , 0 );
// 一些临时变量,用于写到excel中
Label l = null ;
jxl.write.Number n = null ;
jxl.write.DateTime d = null ;
// 预定义的一些字体和格式,同一个Excel中最好不要有太多格式
WritableFont headerFont = new WritableFont(WritableFont.ARIAL, 12 , WritableFont.BOLD,
false , Underlinestyle.NO_UNDERLINE, jxl.format.Colour.BLUE);
WritableCellFormat headerFormat = new WritableCellFormat (headerFont);
WritableFont titleFont = new WritableFont(WritableFont.ARIAL, 10 , WritableFont.NO_BOLD,
false , Underlinestyle.NO_UNDERLINE, jxl.format.Colour.RED);
WritableCellFormat titleFormat = new WritableCellFormat (titleFont);
WritableFont detFont = new WritableFont(WritableFont.ARIAL, 10 , WritableFont.NO_BOLD,
false , Underlinestyle.NO_UNDERLINE, jxl.format.Colour.BLACK);
WritableCellFormat detFormat = new WritableCellFormat (detFont);
NumberFormat nf = new NumberFormat( " 0.00000 " ); // 用于Number的格式
WritableCellFormat priceFormat = new WritableCellFormat (detFont, nf);
DateFormat df = new DateFormat( " yyyy-MM-dd " ); // 用于日期的
WritableCellFormat dateFormat = new WritableCellFormat (detFont, df);
// 剩下的事情,就是用上面的内容和格式创建一些单元格,再加到sheet中
l = new Label( 0 , 0 , " 用于测试的Excel文件 " , headerFormat);
sheet.addCell(l);
// add Title
int column = 0 ;
l = new Label(column ++ , 2 , " 标题 " , titleFormat);
sheet.addCell(l);
l = new Label(column ++ , 2 , " 日期 " , titleFormat);
sheet.addCell(l);
l = new Label(column ++ , 2 , " 货币 " , titleFormat);
sheet.addCell(l);
l = new Label(column ++ , 2 , " 价格 " , titleFormat);
sheet.addCell(l);
// add detail
int i = 0 ;
column = 0 ;
l = new Label(column ++ , i + 3 , " 标题 " + i, detFormat);
sheet.addCell(l);
d = new DateTime(column ++ , i + 3 , new java.util.Date(), dateFormat);
sheet.addCell(d);
l = new Label(column ++ , i + 3 , " CNY " , detFormat);
sheet.addCell(l);
n = new jxl.write.Number(column ++ , i + 3 , 5.678 , priceFormat);
sheet.addCell(n);
i ++ ;
column = 0 ;
l = new Label(column ++ , i + 3 , " 标题 " + i, detFormat);
sheet.addCell(l);
d = new DateTime(column ++ , i + 3 , new java.util.Date(), dateFormat);
sheet.addCell(d);
l = new Label(column ++ , i + 3 , " SGD " , detFormat);
sheet.addCell(l);
n = new jxl.write.Number(column ++ , i + 3 , 98832 , priceFormat);
sheet.addCell(n);
// 设置列的宽度
column = 0 ;
sheet.setColumnView(column ++ , 20 );
sheet.setColumnView(column ++ , 20 );
sheet.setColumnView(column ++ , 10 );
sheet.setColumnView(column ++ , 20 );
workbook.write();
workbook.close();
WritableWorkbook workbook = Workbook.createWorkbook(tempFile);
WritableSheet sheet = workbook.createSheet( " TestCreateExcel " , 0 );
// 一些临时变量,用于写到excel中
Label l = null ;
jxl.write.Number n = null ;
jxl.write.DateTime d = null ;
// 预定义的一些字体和格式,同一个Excel中最好不要有太多格式
WritableFont headerFont = new WritableFont(WritableFont.ARIAL, 12 , WritableFont.BOLD,
false , Underlinestyle.NO_UNDERLINE, jxl.format.Colour.BLUE);
WritableCellFormat headerFormat = new WritableCellFormat (headerFont);
WritableFont titleFont = new WritableFont(WritableFont.ARIAL, 10 , WritableFont.NO_BOLD,
false , Underlinestyle.NO_UNDERLINE, jxl.format.Colour.RED);
WritableCellFormat titleFormat = new WritableCellFormat (titleFont);
WritableFont detFont = new WritableFont(WritableFont.ARIAL, 10 , WritableFont.NO_BOLD,
false , Underlinestyle.NO_UNDERLINE, jxl.format.Colour.BLACK);
WritableCellFormat detFormat = new WritableCellFormat (detFont);
NumberFormat nf = new NumberFormat( " 0.00000 " ); // 用于Number的格式
WritableCellFormat priceFormat = new WritableCellFormat (detFont, nf);
DateFormat df = new DateFormat( " yyyy-MM-dd " ); // 用于日期的
WritableCellFormat dateFormat = new WritableCellFormat (detFont, df);
// 剩下的事情,就是用上面的内容和格式创建一些单元格,再加到sheet中
l = new Label( 0 , 0 , " 用于测试的Excel文件 " , headerFormat);
sheet.addCell(l);
// add Title
int column = 0 ;
l = new Label(column ++ , 2 , " 标题 " , titleFormat);
sheet.addCell(l);
l = new Label(column ++ , 2 , " 日期 " , titleFormat);
sheet.addCell(l);
l = new Label(column ++ , 2 , " 货币 " , titleFormat);
sheet.addCell(l);
l = new Label(column ++ , 2 , " 价格 " , titleFormat);
sheet.addCell(l);
// add detail
int i = 0 ;
column = 0 ;
l = new Label(column ++ , i + 3 , " 标题 " + i, detFormat);
sheet.addCell(l);
d = new DateTime(column ++ , i + 3 , new java.util.Date(), dateFormat);
sheet.addCell(d);
l = new Label(column ++ , i + 3 , " CNY " , detFormat);
sheet.addCell(l);
n = new jxl.write.Number(column ++ , i + 3 , 5.678 , priceFormat);
sheet.addCell(n);
i ++ ;
column = 0 ;
l = new Label(column ++ , i + 3 , " 标题 " + i, detFormat);
sheet.addCell(l);
d = new DateTime(column ++ , i + 3 , new java.util.Date(), dateFormat);
sheet.addCell(d);
l = new Label(column ++ , i + 3 , " SGD " , detFormat);
sheet.addCell(l);
n = new jxl.write.Number(column ++ , i + 3 , 98832 , priceFormat);
sheet.addCell(n);
// 设置列的宽度
column = 0 ;
sheet.setColumnView(column ++ , 20 );
sheet.setColumnView(column ++ , 20 );
sheet.setColumnView(column ++ , 10 );
sheet.setColumnView(column ++ , 20 );
workbook.write();
workbook.close();
下面定义了很多样式,好像速度也不算慢,还有一些其他方面的定义,补充到下面:
//
构造格式:ARIAL字体、10号、粗体、非斜体、无下划线、黑色
WritableFont fmtx2TotalCaption = new WritableFont(WritableFont.ARIAL, 10 ,WritableFont.BOLD,
false ,UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.BLACK);
WritableCellFormat totalx2Format = new WritableCellFormat(fmtx2TotalCaption);
// 文字垂直居中对齐
totalx2Format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
// 文字水平居中对齐
totalx2Format.setAlignment(jxl.format.Alignment.CENTRE);
// 边框深蓝色
totalx2Format.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN,
jxl.format.Colour.DARK_BLUE);
// 设置底色为冰蓝
totalx2Format.setBackground(jxl.format.Colour.ICE_BLUE);
sheet.mergeCells( 0 , row, 8 , row); // 合并单元格,row 到 row 行,0 到 8 列
sheet.setRowView(row, 600 ); // 第 row 行的高度
WritableFont fmtx2TotalCaption = new WritableFont(WritableFont.ARIAL, 10 ,WritableFont.BOLD,
false ,UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.BLACK);
WritableCellFormat totalx2Format = new WritableCellFormat(fmtx2TotalCaption);
// 文字垂直居中对齐
totalx2Format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
// 文字水平居中对齐
totalx2Format.setAlignment(jxl.format.Alignment.CENTRE);
// 边框深蓝色
totalx2Format.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN,
jxl.format.Colour.DARK_BLUE);
// 设置底色为冰蓝
totalx2Format.setBackground(jxl.format.Colour.ICE_BLUE);
sheet.mergeCells( 0 , row, 8 , row); // 合并单元格,row 到 row 行,0 到 8 列
sheet.setRowView(row, 600 ); // 第 row 行的高度
另外就是要在 Action(servlet) 中这么写,IE就会直接弹出保存文件的对话框(注意要占用当前窗口):
String title
=
"
XXXX统计
"
;
OutputStream out = response.getOutputStream();
WritableWorkbook wb = Workbook.createWorkbook(out);
response.setContentType( " aplication/vnd.ms-excel " );
response.addHeader( " Content-Disposition " , " inline; filename= " + new String(title.getBytes( " GB2312 " ), " ISO8859_1 " ) + " .xls " ); // 有中文必须转码
OutputStream out = response.getOutputStream();
WritableWorkbook wb = Workbook.createWorkbook(out);
response.setContentType( " aplication/vnd.ms-excel " );
response.addHeader( " Content-Disposition " , " inline; filename= " + new String(title.getBytes( " GB2312 " ), " ISO8859_1 " ) + " .xls " ); // 有中文必须转码