Spring实现的动态文件下载(以Excel导出为例)

xiaoxiao2021-02-28  12



根据请求动态产生文件。以导出Excel文件为例。页面查询结果POST提交给服务器,生成Excel文件,返回浏览器弹出下载框。 方法一:产生临时文件的方式(ajax提交) 由于ajax的返回类型(dataType)只有xml、text、json、html等类型,没有“流”类型.所以不能通过将Excel文件写入Response对象的方式下载。 只能在服务器的临时目录生成目标Excel文件,再将其地址返回给浏览器,实现下载。 前端js代码         

$.ajax({ type : 'POST', url : “/download”, // 处理请求的url data : "postInfo=" + JSON.stringify(js), // 提交给服务器的数据 async : false, success : function(data){ window.open(baseURL + data); // baseURL是工程名,例:/project1 } }); 后台控制器代码:      /** * 根据JSON格式的字符串生成xlsx文件 * @param printWriter * @param req * @throws IOException */ @RequestMapping(value = "/download", method = RequestMethod.POST) public void saveAsXlsx(HttpServletRequest req, PrintWriter pw) { // 使用apache的POI,根据提交过来的数据(从HttpServletRequest中取得) // 生成目标Excel文件,放在"工程目录/WebContent/WEB-INF/upload"下 // 下面直接返回地址,实际项目中要动态产生地址 pw.write("/tmp/filename.xlsx"); pw.flush(); pw.close(); } spring 的配置文件要加入静态文件访问权限: <mvc:resources location="/WEB-INF/tmp/" mapping="/tmp/**"/> 工作原理:ajax提交POST请求到控制器“saveAsXlsx”方法生成目标Excel文件,PrintWriter对象返回文件路径,客户端判断服务器返回状态为success,于是调用success指向函数,执行window.open(baseURL + data),提交资源请求“工程/tmp/filename.xlsx”,spring 查看配置文件发现配置,<mvc:resources location="/WEB-INF/tmp/" mapping="/tmp/**"/>,直接将请求资源返回浏览器,浏览器根据设置判断是打开,还是下载。 第二种方法:用spring framework的AbstractXlsxStreamingView类实现(指定结果集)导出为Excel(xlsx) 先百度,找到AbstractExcelView类,但帮助文件里已经标上了Deprecated,推荐Spring 4.2以后使用 AbstractXlsView and its AbstractXlsxView and AbstractXlsxStreamingView。 前端js代码      function saveDataAs() { var js={}; // 待传向后台的JSON对象 js.filenameSaveAs=$("#spanFilenameSaveAs").html() + "产量统计"; // 待保存的文件名 // 根据具体业务准备待提交的数据,此部分代码根据实际添加... var postData = JSON.stringify(js); // 准备好提交的数据 var urlGo = baseURL + "/index/saveAsXlsx"; download(urlGo,postData); } /** * 代码生成表单(实际提交请求由此表单发起),提交下载请求 * @param url * @param postData */ function download(url,postData) { var form = $("<form>"); // 定义一个form表单 form.attr('style', 'display:none'); // 在form表单中添加查询参数 form.attr('target', ''); form.attr('method', 'post');// 使用POST方式提交 form.attr('action', url); var input1 = $('<input>'); input1.attr('type', 'hidden'); input1.attr('name', 'postInfo'); input1.attr('value', postData); $('body').append(form); //将表单放置在web中 form.append(input1); //将查询参数控件添加到表单上 form.submit(); }后台定义Excel文件处理类,给spring返回用: // 导入相应包 ... import org.springframework.web.servlet.view.document.AbstractXlsxStreamingView; public class MyXlsxView extends AbstractXlsxStreamingView { @Override protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest req, HttpServletResponse resp) throws Exception { // 取前台提交的数据 String postInfo = req.getParameter("postInfo"); JSONObject jsPI = new JSONObject(postInfo); // 标题栏 JSONArray jsT = jsPI.getJSONArray("thead"); // 文件名 String filename = jsPI.getString("filenameSaveAs") + ".xlsx"; // 取下载时客户端Excel的名称 SXSSFWorkbook sxssWb = (SXSSFWorkbook)workbook; Sheet sheet = sxssWb.createSheet("sheet1"); int iniRow = 0; Row row = sheet.createRow((short) iniRow); Cell cell = null; // 添加表格标题行 for(int i = iniRow; i < jsT.length(); ++i){ cell = row.createCell(i); cell.setCellType(SXSSFCell.CELL_TYPE_STRING); cell.setCellValue(jsT.getString(i)); } ++iniRow; // 标题行完成,下移一行 // 商品内容 JSONArray sc = jsPI.getJSONArray("pdInfo"); // 商品的一行 JSONObject jsL = null; // 添加表格内容 for(int pdRow = iniRow; pdRow < sc.length() + iniRow; ++pdRow){ row = sheet.createRow(pdRow); // 在指定行创建一个Row对象 jsL = sc.getJSONObject(pdRow-iniRow); int col = 0; cell = row.createCell(col++); cell.setCellType(SXSSFCell.CELL_TYPE_STRING); cell.setCellValue(jsL.getString("barCode")); cell = row.createCell(col++); cell.setCellValue(jsL.getString("styleCode")); ... } //处理中文文件名 String reqCharset = req.getCharacterEncoding(); /*根据request的getCharacterEncoding得到请求时的编码*/ filename = new String(filename.getBytes(reqCharset), "ISO8859-1"); resp.setCharacterEncoding(reqCharset); // 若想下载时自动填好文件名,则需要设置响应头的"Content-disposition" resp.setHeader("Content-disposition", "attachment;filename=" + filename); } }后台控制器代码:      /** * 根据JSON格式的字符串生成xlsx文件 */ @RequestMapping(value = "index/saveAsXlsx", method = RequestMethod.POST) public ModelAndView saveAsXlsx(ModelMap model){ MyXlsxView ve = new MyXlsxView(); return new ModelAndView(ve, model); } 关于抽象类AbstractXlsxStreamingView的用法, 这个类要求用户继承此类,并实现buildExcelDocument()方法, 在此方法中构建待返回的Excel文件, 如果希望客户端保存文件具有默认的文件名, 则需设置响应头的"Content-disposition"属性(对应代码:resp.setHeader("Content-disposition", "attachment;filename=" + filename);),如果不设置则spring会使用客户端提交请求时的url(本例中提交的是baseURL + "/index/saveAsXlsx", 则spring会截取后面的saveAsXlsx作为默认文件名).

当控制器返回时(return new ModelAndView(ve, model);), Spring负责调用,用户类的buildExcelDocument()方法, 把此方法参数workbook所指向的实际对象返回给浏览器,供用户下载. 第一种方法的优点是逻辑容易理解,缺点是生成了临时文件,之后还要处理(不然就会越来越多),程序不够优雅. 第二种方法的优点是程序组织非常优雅,控制器代码量很少,缺点(其实也不算真正的缺点)是需要了解AbstractXlsxStreamingView的工作原理.

转载请注明原文地址: https://www.6miu.com/read-1650315.html

最新回复(0)