根据请求动态产生文件。以导出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();
}
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 = $("
后台定义Excel文件处理类,给spring返回用:
// 导入相应包
...
import org.springframework.web.servlet.view.document.AbstractXlsxStreamingView;
public class MyXlsxView extends AbstractXlsxStreamingView {
@Override
protected void buildExcelDocument(Map 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);
}
当控制器返回时(return new ModelAndView(ve, model);), Spring负责调用,用户类的buildExcelDocument()方法, 把此方法参数workbook所指向的实际对象返回给浏览器,供用户下载.
第一种方法的优点是逻辑容易理解,缺点是生成了临时文件,之后还要处理(不然就会越来越多),程序不够优雅.
第二种方法的优点是程序组织非常优雅,控制器代码量很少,缺点(其实也不算真正的缺点)是需要了解AbstractXlsxStreamingView的工作原理.