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



根据请求动态产生文件。以导出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 的配置文件要加入静态文件访问权限:

工作原理:ajax提交POST请求到控制器“saveAsXlsx”方法生成目标Excel文件,PrintWriter对象返回文件路径,客户端判断服务器返回状态为success,于是调用success指向函数,执行window.open(baseURL + data),提交资源请求“工程/tmp/filename.xlsx”,spring 查看配置文件发现配置,,直接将请求资源返回浏览器,浏览器根据设置判断是打开,还是下载。

第二种方法:用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.attr('style', 'display:none'); // 在form表单中添加查询参数 form.attr('target', ''); form.attr('method', 'post');// 使用POST方式提交 form.attr('action', url); var input1 = $(''); 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 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的工作原理.

你可能感兴趣的:(Spring实现的动态文件下载(以Excel导出为例))