一、功能说明:
模拟页面选择多项文件进行打包下载的过程。这里生成3个Excel文件,打包下载。可以选择在服务器上保留打包文件,也可以选择不保留打包文件。
二、涉及的Struts2 下载文件配置
<package name="test" namespace="/pages" extends="struts-default"> <!-- [法一]打包Excel,并导出,保留zip文件,删除所有Excel临时文件 --> <action name="testAction" class="com.test.action.TestAction"> <result name="export" type="stream"> <param name="contentType"> application/x-zip-compressed;charset=UTF-8 </param> <param name="inputName">inputStream</param> <param name="contentDisposition"> attachment;filename="${fileName}" </param> <param name="bufferSize">1024</param> </result> <!-- 失败时 --> <result name="nodata" type="httpheader"> <param name="status">204</param> </result> </action> <!-- [法二]打包Excel,并导出,不产生zip文件,而是构建zip文件的输出流,删除所有Excel临时文件 --> <action name="testAction2" class="com.test.action.TestAction" method="execute2"> <!-- 成功时自动就输出了,无result --> <!-- 失败时 --> <result name="nodata" type="httpheader"> <param name="status">204</param> </result> </action> </package>
【说明】
(1)结果类型必须要写成 type="stream" ,与之对应的处理类是 org.apache.struts2.dispatcher.StreamResult,
其涉及到的参数:(图片来自网络)
(2) contentType属性
网页中的Content-Type,指定响应的 HTTP内容类型,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件。
.zip 的Content-Type是 application/x-zip-compressed
.rar 的Content-Type是 application/octet-stream
.xls 的Content-Type是 application/msexel
(3)inputName属性
指定该属性值为“inputStream”,将调用action的getInputStream方法来获取要下载的文件输出流。
(4)contentDisposition属性
Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
格式说明:
content-disposition = "Content-Disposition" ":" disposition-type *( ";" disposition-parm )
字段说明:
Content-Disposition为属性名
disposition-type是以什么方式下载,如attachment为以附件方式下载
disposition-parm为默认保存时的文件名
服务端向客户端浏览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment
三、jxl生成Excel 以及 多文件打包生成zip文件
法一:打包Excel,并导出,保留zip文件,删除所有Excel临时文件
TestAction.java:
package com.test.action; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.servlet.http.HttpServletResponse; import jxl.Workbook; import jxl.format.UnderlineStyle; import jxl.write.Label; import jxl.write.NumberFormats; import jxl.write.WritableCellFormat; import jxl.write.WritableFont; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; /* * 批量生成Excel 并打包(zip文件)下载 */ public class TestAction extends ActionSupport { private String fileName; // 记录下载文件的默认名 private FileInputStream inputStream; // 要下载的文件输出流 /** * 【法一】 * 打包Excel,并导出,保留zip文件,删除所有Excel临时文件 * @throws Exception */ @Override public String execute() throws Exception { // 设置导出文件存放地址 String fileUrl = getExportUrl(null,"zip",true); // 生成Excel,返回Excel的File类(保存了生成的Excel文件路径) File[] files = getExcelFiles(); if (files == null || files.length <= 0) { return "nodata"; } // 打包,放到服务器固定输出路径下,永久保留 packFilesZip(files, fileUrl); // 下载压缩文件的默认名字 fileName = new String("测试压缩文件.zip".getBytes("GBK"), "ISO-8859-1"); // 构建打包文件的输出流,用于下载 inputStream = new FileInputStream(fileUrl); return "export"; } /** * 该程序服务器路径\export\当前时间-warn.后缀名 * * @param fileName * 文件名 * @param suffix * 后缀名 * @param isAutoGenerateFileName * 是否使用当前时间作为文件名 * @return */ private String getExportUrl(String fileName, String suffix, Boolean isAutoGenerateFileName) { String saveDir = ServletActionContext.getServletContext().getRealPath( File.separator) + File.separator + "export"; File dir = new File(saveDir); if (!dir.exists()) { dir.mkdirs(); } String fileUrl = null; if (isAutoGenerateFileName) { // 使用当前时间自动生成文件名 String curTime = new SimpleDateFormat("yy-MM-dd-HH-mm-ss-SSS") .format(new Date()); fileUrl = saveDir + File.separator + curTime + "-warn." + suffix.trim(); } else { // 使用自定义文件名 fileUrl = saveDir + File.separator + fileName.trim() + "." + suffix.trim(); } return fileUrl; } /** * 文件打包 (保留zip文件) * * @param files * 要打包的文件列表 * @param fileUrl * 打包存放位置 */ private void packFilesZip(File[] files, String fileUrl) throws Exception { FileInputStream fis; int len; // 每次存放读取文件的字节内容 byte[] buffer = new byte[1024]; // 每次读取文件的缓存 // 构建压缩包文件输出流,根据路径生成压缩包文件 ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(fileUrl)); // 遍历要放入压缩包内的文件 for (int i = 0; i < files.length; i++) { if (!files[i].exists()) { System.out.print("error" + i); continue; } // 用于读取本地文件信息 fis = new FileInputStream(files[i]); // 在压缩包内构建一个与本地文件一样命名的实体文件 ZipEntry ze = new ZipEntry(files[i].getName()); ze.setSize(files[i].length()); zos.putNextEntry(ze); // 边读本地文件,边向压缩包内的同名实体文件复制(写)数据 while ((len = fis.read(buffer)) != -1) { // 向压缩包内的同名实体文件复制(写)数据 // int len存放读取到的文件字节内容 // byte[] buffer 缓存 zos.write(buffer, 0, len); } zos.closeEntry(); fis.close(); // 打完包把文件删除掉 if (files[i].exists()) { files[i].delete(); } } zos.close(); // 不写这个会报“不可预料的压缩文件末端”的异常 } // 获取struts文件输出流 private OutputStream getOutputStream(String fileFullName) throws IOException { HttpServletResponse response = ServletActionContext.getResponse(); OutputStream res = response.getOutputStream(); //清空输出流 response.reset(); //设定输出文件头 fileFullName如“a.zip”,如果是中文名,必须URLEncoder编码 response.setHeader("Content-disposition ","attachment; filename=" + URLEncoder.encode(fileFullName, "UTF-8")); response.setContentType("application/zip"); response.setCharacterEncoding("UTF-8"); return res; } /** * 批量生成Excel * * @return 所有Excel的File文件列表 * @throws Exception */ private File[] getExcelFiles() throws Exception { File f1 = getExcel(getExportUrl("ex1", "xls", false)); File f2 = getExcel(getExportUrl("ex2", "xls", false)); File f3 = getExcel(getExportUrl("ex3", "xls", false)); return new File[] { f1, f2, f3 }; } /** * 生成单个Excel * * @param fileName * 生成的Execel文件名 * @return 该生成的Excel的文件类 * @throws Exception */ private File getExcel(String fileName) throws Exception { File file = new File(fileName); // 打开文件 WritableWorkbook book = Workbook.createWorkbook(file); // 生成名为“第一页”的工作表,参数0表示这是第一页 WritableSheet sheet = book.createSheet(" 第一页 ", 0); // 定义单元格样式 WritableFont wf_title = new WritableFont(WritableFont.ARIAL, 11, WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.BLACK); // 字体格式参数含义: 字体,大小,粗体,斜体,下划线,颜色 WritableCellFormat wcf_title = new WritableCellFormat(wf_title); // 单元格格式(按上面字体样式) wcf_title.setBackground(jxl.format.Colour.WHITE); // 设置单元格的背景颜色 wcf_title.setAlignment(jxl.format.Alignment.CENTRE); // 设置对齐方式 wcf_title.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN, jxl.format.Colour.BLACK); // 设置边框 // 设置第一列的列宽 sheet.setColumnView(0, 15); // 在Label对象的构造子中指名单元格位置是第一列第一行(0,0),单元格内容为test,使用单元格样式 Label label = new Label(0, 0, " test ", wcf_title); // 将定义好的单元格添加到工作表中 sheet.addCell(label); /* * 生成一个保存数字的单元格 必须使用Number的完整包路径,否则有语法歧义 单元格位置是第二列,第一行,值为555.12541 */ WritableCellFormat floatFormat = new WritableCellFormat( NumberFormats.FLOAT); jxl.write.Number number = new jxl.write.Number(1, 0, 555.12541, floatFormat); sheet.addCell(number); // 写入数据并关闭文件 book.write(); book.close(); return file; } }
法二:打包Excel,并导出,不产生zip文件,而是构建zip文件的输出流,删除所有Excel临时文件
在上面的action中加上
/** * 【法二】 * 打包Excel,并导出,不产生zip文件,而是构建zip文件的输出流,删除所有Excel临时文件 * @throws Exception */ public String execute2() throws Exception { // 生成Excel,返回Excel的File类(保存了生成的Excel文件路径) File[] files = getExcelFiles(); if (files == null || files.length <= 0) { return "nodata"; } // 打包,删除Excel临时文件,打的压缩包放入Http文件输出流中,本地不保留任何文件 packFilesZip2(files, "测试压缩文件.zip"); return null; } /** * 文件打包 (不留zip文件,构建zip文件输出流) * * @param files * 要打包的文件列表 * @param fileFullName * 文件全名 如"a.zip" */ private void packFilesZip2(File[] files, String fileFullName) throws Exception { FileInputStream fis; int len; // 每次存放读取文件的字节内容 byte[] buffer = new byte[1024]; // 每次读取文件的缓存 // 构建http文件输出流,将生成的文件压缩包直接写入输出流中,并不在本地保留压缩文件 OutputStream outputStream = getOutputStream(fileFullName); ZipOutputStream zos = new ZipOutputStream(outputStream); // 遍历要放入压缩包内的文件 for (int i = 0; i < files.length; i++) { if (!files[i].exists()) { System.out.print("error" + i); continue; } // 用于读取本地文件信息 fis = new FileInputStream(files[i]); // 在压缩包内构建一个与本地文件一样命名的实体文件 ZipEntry ze = new ZipEntry(files[i].getName()); ze.setSize(files[i].length()); zos.putNextEntry(ze); // 边读本地文件,边向压缩包内的同名实体文件复制(写)数据 while ((len = fis.read(buffer)) != -1) { // 向压缩包内的同名实体文件复制(写)数据 // int len存放读取到的文件字节内容 // byte[] buffer 缓存 zos.write(buffer, 0, len); } zos.closeEntry(); fis.close(); // 打完包把文件删除掉 if (files[i].exists()) { files[i].delete(); } } zos.close(); // 不写这个会报“不可预料的压缩文件末端”的异常 outputStream.close(); }
访问方式:
1、在/export文件夹下保留了压缩文件
http://localhost:8080/justStruts/pages/testAction.action
2、不保留任何文件
http://localhost:8080/justStruts/pages/testAction2.action