struts文件下载机制
Struts2 中使用 type="stream" 的 result 进行下载即可。只用提供一个输入流inputStream,剩下的输出工作struts帮我们做。
例子一:
1.可以为 stream 的 result 设定如下参数
contentType: 结果类型
contentLength: 下载的文件的长度
contentDisposition: 设定 Content-Dispositoin 响应头. 该响应头指定响应是一个文件下载类型, 一般取值为 attachment;filename="document.pdf".
inputName: 指定文件输入流的 getter 定义的那个属性的名字. 默认为 inputStream
这四个一般在getter方法中定义或者在execute方法执行时设置,而且不用声明变量,直接getter方法,第一个字母大写,例如public InputStream getInputStream()。
bufferSize: 缓存的大小. 默认为 1024
allowCaching: 是否允许使用缓存
contentCharSet: 指定下载的字符集
这三个参数一般使用默认值或者在xml文件中配置。
以上参数可以在 Action 中以 getter 方法的方式提供,也可以通过配置配置,也可以使用默认值。也可以动态设置值,在execute方法中设置。
1 package FileDownload; 2 3 import java.io.FileInputStream; 4 import java.io.InputStream; 5 6 import javax.servlet.ServletContext; 7 8 import org.apache.struts2.ServletActionContext; 9 10 import com.opensymphony.xwork2.ActionSupport; 11 12 public class FileDownLoad extends ActionSupport { 13 14 /** 15 * 16 */ 17 private static final long serialVersionUID = 1L; 18 19 private String contentType; 20 private long contentLength; 21 private String contentDisposition; 22 private InputStream inputStream; 23 24 25 public String getContentType() { 26 return contentType; 27 } 28 29 30 public long getContentLength() { 31 return contentLength; 32 } 33 34 35 // 在getter方法中设置所需要的参数 36 public String getContentDisposition() { 37 contentDisposition = "attachment;filename=filesUp.html"; 38 return contentDisposition; 39 } 40 41 42 public InputStream getInputStream() { 43 return inputStream; 44 } 45 46 47 @Override 48 public String execute() throws Exception { 49 50 //确定各个成员变量的值 51 contentType = "text/html"; 52 53 ServletContext servletContext = 54 ServletActionContext.getServletContext(); 55 String fileName = servletContext.getRealPath("/files/filesUp.html"); 56 // 打开输入流 57 inputStream = new FileInputStream(fileName); 58 contentLength = inputStream.available(); 59 60 61 return super.execute(); 62 } 63 }
上面可以在execute方法执行时动态设置参数,也可以在getter方法中设置参数。
配置文件
<!-- 文件下载 --> <action name="fileDownload" class="FileDownload.FileDownLoad"> <result type="stream"> <!-- 其他的参数在类中设置或者使用默认 --> <param name="bufferSize">2048</param> </result> </action>
例子二:
目录结构:
动态将数据写到Excel文件并提供下载:
package cn.xm.exam.action.exam.exam; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.sql.SQLException; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.apache.commons.io.FileUtils; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFFont; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.struts2.ServletActionContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.opensymphony.xwork2.ActionSupport; import cn.xm.exam.MyElFunction.MyElFunction; import cn.xm.exam.service.exam.exam.ExamService; import cn.xm.exam.service.grade.EmployeeExamService; import jxl.common.Logger; /** * 导出参考人信息 1.查出数据 2.写入Excel 3.打开流,提供下载 * * @author QiaoLiQiang * @time 2017年10月29日下午10:29:51 */ @Controller @Scope("prototype") @SuppressWarnings("all") public class ExtExamEmployeesAction extends ActionSupport { private Logger logger = Logger.getLogger(FindExamAction.class); private String fileName;// 导出的Excel名称 @Resource private ExamService examService;// 考试service @Resource private EmployeeExamService employeeExamService;// 员工成绩service // 1.查数据 private String examId;// 考试ID(用于查询考试员工) public List<Map<String, Object>> findExamEmployeeByExamId() { List<Map<String, Object>> employees = null; try { employees = employeeExamService.getEmployeeexamsByExamId(examId); } catch (SQLException e) { logger.error("导出考试人员异常", e); } return employees; } // 2.写入Excel public boolean writeExamEmployees2Excel(List<Map<String, Object>> examEmployees, String fileName) { // 标题 String[] title = { "序号", "姓名", "身份证号", "性别", "所属部门", "员工类型", "考试方式" }; // 创建一个工作簿 HSSFWorkbook workbook = new HSSFWorkbook(); // 创建一个工作表sheet HSSFSheet sheet = workbook.createSheet(); // 设置列宽 setColumnWidth(sheet, 7); // 创建第一行 HSSFRow row = sheet.createRow(0); // 创建一个单元格 HSSFCell cell = null; // 创建表头 for (int i = 0; i < title.length; i++) { cell = row.createCell(i); // 设置样式 HSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 设置字体居中 // 设置字体 HSSFFont font = workbook.createFont(); font.setFontName("宋体"); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);// 字体加粗 // font.setFontHeight((short)12); font.setFontHeightInPoints((short) 13); cellStyle.setFont(font); cell.setCellStyle(cellStyle); cell.setCellValue(title[i]); } // 写入数据 // 从第二行开始追加数据 for (int i = 1; i < (examEmployees.size() + 1); i++) { // 创建第i行 HSSFRow nextRow = sheet.createRow(i); // 获取数据 Map<String, Object> employees = examEmployees.get(i - 1); for (int j = 0; j < 7; j++) { HSSFCell cell2 = nextRow.createCell(j); if (j == 0) { cell2.setCellValue(j + 1); } if (j == 1) { cell2.setCellValue(employees.get("employeeName").toString()); } if (j == 2) { cell2.setCellValue(employees.get("employeeId").toString()); } if (j == 3) { cell2.setCellValue(MyElFunction.replace(employees.get("sex").toString(), "12", "男女")); } if (j == 4) { cell2.setCellValue(employees.get("departmentName").toString()); } if (j == 5) { cell2.setCellValue( MyElFunction.replace(employees.get("employeeType").toString(), "01", "内外") + "部员工"); } if (j == 6) { cell2.setCellValue(employees.get("examMethod").toString()); } } } // 创建一个文件 File file = new File(fileName); // 如果存在就删除 if (file.exists()) { file.delete(); } try { file.createNewFile(); // 打开文件流 FileOutputStream outputStream = FileUtils.openOutputStream(file); workbook.write(outputStream); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } return true; } // 设置列宽() private static void setColumnWidth(HSSFSheet sheet, int colNum) { for (int i = 0; i < colNum; i++) { int v = 0; v = Math.round(Float.parseFloat("15.0") * 37F); v = Math.round(Float.parseFloat("20.0") * 267.5F); sheet.setColumnWidth(i, v); } } // 3.打开文件的流提供下载 public InputStream getInputStream() throws Exception { this.create();// 创建文件到指定目录下 String path = ServletActionContext.getServletContext().getRealPath("/files/examEmployeesExcel"); String filepath = path + "\\" + fileName + ".xls"; File file = new File(filepath); // 只用返回一个输入流 return FileUtils.openInputStream(file);// 打开文件 } // 产生Excel到文件夹下面 public void create() { // 获取工程下的路径 String path = ServletActionContext.getServletContext().getRealPath("/files/examEmployeesExcel"); String filepath = path + "\\" + fileName + ".xls"; // 获取文件 List<Map<String, Object>> examEmployees = this.findExamEmployeeByExamId(); this.writeExamEmployees2Excel(examEmployees, filepath); } // 文件下载名 public String getDownloadFileName() { String downloadFileName = ""; String filename = fileName + ".xls"; try { downloadFileName = URLEncoder.encode(filename, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return downloadFileName; } @Override public String execute() throws Exception { // 先将名字设为秒数产生唯一的名字 this.setFileName(String.valueOf(System.currentTimeMillis())); return super.execute(); } // get,set方法 public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getExamId() { return examId; } public void setExamId(String examId) { this.examId = examId; } }
配置文件:
<!-- 导出参考人员信息 --> <action name="exportExamEmployees" class="extExamEmployeesAction"> <result type="stream"> <!-- 其他的参数在类中设置或者使用默认 --> <param name="contentType">application/octet-stream</param> <param name="inputName">inputStream</param> <param name="contentDisposition">attachment;filename="${downloadFileName}"</param> <param name="bufferSize">8192</param> </result> </action>
${downloadFileName}会调用getDownloadFileName()方法
例子三:
目录结构:
1.前台JS
<script type="text/javascript"> function down(flag) { if (flag == 1) { if (confirm("确定下载谷歌浏览器?")) { window.location.href = "export_execute.action?name=ChromeStandalone_56.0.2924.87_Setup.exe" } } if (flag == 2) { if (confirm("确定下载火狐浏览器?")) { window.location.href = "export_execute.action?name=Firefox-57.0.2.6549-setup.exe" } } if (flag == 3) { if (confirm("确定下载操作手册?")) { window.location.href = "export_execute.action?name=《安全培训管理系统》用户考试操作手册.pdf" } } } </script>
2.后台Action(tomcat编码是UTF-8)
package cn.xm.exam.action.exam.exam;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import org.apache.struts2.ServletActionContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import com.opensymphony.xwork2.ActionSupport;
/**
* 下载专区的下载文件
*
* @author QiaoLiQiang
* @time 2017年12月14日上午10:06:41
*/
@Controller // 控制层
@Scope("prototype") // 单例模式
@SuppressWarnings("all") // 压制警告
public class ExtDownloadFile extends ActionSupport {
// 注入服务器目录地址
private String serverPath;// 注入要下载的文件的地址
// 接收文件名
private String name;// struts的属性驱动
public void setServerPath(String serverPath) {
this.serverPath = serverPath;
}
public String getServerPath() {
return serverPath;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 需要提供给struts写出数据的输入流
public InputStream getInputStream() {
try {
// 转码:解决中文乱码问题
// 先用ISO8859-1编码 再使用UTF-8解码
// String filename = new
// String(name.getBytes("ISO8859-1"),"UTF-8");//中名名称.后缀名
String filename = name;
String path = ServletActionContext.getServletContext().getRealPath(serverPath);
FileInputStream fis = new FileInputStream(new File(path + "\\" + filename));// 服务器目录地址+文件名
name = new String(name.getBytes("UTF-8"), "ISO8859-1");
return fis;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
// 下载方法
public String execute() {
return SUCCESS;
}
}
3.配置:
<!-- 下载专区的文件下载 --> <action name="export_*" class="extDownloadFile"> <!-- 为setServerPath方法中的serverPath属性注入值 也就是文件存放的地址/目录(文件应该从哪里下载) --> <param name="serverPath">/files/downloadFiles</param> <!-- 文件下载的关键:视图类型一定是stream --> <result type="stream"> <!-- 往StreamResult类中的属性注入内容 --> <!-- 返回给浏览器的文件类型。返回通用的二进制 --> <param name="contentType">application/octet-stream</param> <!-- 返回给浏览器的输入流 --> <!-- 默认就是 inputStream,它将会指示 StreamResult 通过 inputName 属性值的 getter 方法, 比如这里就是 getInputStream() 来获取下载文件的内容,意味着你的 Action 要有这个方法 --> <param name="inputName">inputStream</param> <!-- 告诉浏览器的方式下载资源 ${name}:获取Action中getName()方法的数据 --> <!-- 默认为 inline(在线打开),设置为 attachment 将会告诉浏览器下载该文件,filename 指定下载文 件保存的文件名,若未指定将会是以浏览的页面名作为文件名,如以 download.action 作为文件名, 这里使用的是动态文件名,${name}, 它将通过 Action 的 getName() 获得文件名 --> <param name="contentDisposition">attachment;filename=${name}</param> <!-- 缓存大小 --> <param name="bufferSize">1024</param> </result> </action>