jquery][struts][poi]异步导出excel

本文介绍了如何在服务器内存中生成ExcelStream并将其返回给客户端,避免了将文件保存在服务器中导致的数据量问题。通过使用AJAX请求接收stream并利用submit方法打开新窗口来实现不刷新当前页面的导出功能,同时讨论了在不使用iframe的情况下如何优化用户体验。此外,文章还涉及了内存导出Excel时可能遇到的数据量限制问题及代码移植性设计的初步考虑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要功能点,提供异步查询数据导出excel功能。
不希望将excel保存在服务器中,所以在服务器的内存中生产excel stream后返回给客户端,
ajax请求接收到stream后无法处理,所以需要使用submit,
常规submit会刷新当前页面
无刷新submit我想过采用有两种:1、target="iframe name" 2、_blank
通过iframe的submit,需要设置submit的callback方法,将页面中给客户看的exporting提示取消掉,更麻烦一些,所以用了_blank,打开一个空的窗口,excel返回后窗口会自动关闭,虽然不算异步查询,但不影响主页的使用,所以soso。
网上说内存导出excel通常会有数据量的问题,这个问题我还没开始考虑。
代码的移植性还没设计。


html
一共有两个页面,report.html 和 excel.html, js框架这里主要用了jquery

excel.html中定义了以下内容:
<form id="excel" style="display:none;" target="_blank" action="../metrics/excel.action">
<input name="forecast_date" />
<input name="wid" />
<input name="product_sort" />
<input name="product_sort_level" />
<input name="dimon" />
<input name="target_selected" />
</form>
form里面的target="_blank",提供了不刷新当前页面的submit效果,action定义了submit的请求路径

report.html中主要编写了export按钮的click事件,先通过ajax请求获取到excel.html,将dom元素append到当前html中,然后使用jquery的submit方法,因为excel.html中form的target是_blank,所以不会刷新report.html这个页面,而是打开一个新窗口。jdForm.setFormValues和jdForm.getFormValues只是填充和获取form里面的内容,在export excel中没有参考价值
$("#export").click(function(e) {
        var obj = jdForm.getFormValues("#form_query");
        if (!jdForm.validate(obj))
                return false;
        $.ajax({
                url : "excel.html",
                success : function(html) {
                        $("form#excel").remove();
                        $("body").append(html);
                        jdForm.setFormValues(obj, $("form#excel"));
                        $("form#excel").submit();
                }
        });
        return false;
});


java使用struts2和poi完成excel导出,poi将数据在内存中做成excel stream,然后交付struts2返回给页面。所以普通的ajax get/post不能直接调用这个action,因为返回的是stream给ajax的callback函数,这样在回调函数中只能获取到一堆乱码。这里需要使用submit方式,浏览器才会将服务端返回的stream作为excel提供给用户下载或打开,应该和http request协议有关,但没有仔细研究过协议,吃了哑巴亏。

struts的配置:
<action name="excel" method="excel" class="com.scm.forecasting.action.metrics.ReportAction">
        <result name="success" type="stream">
                <param name="contentType">application/vnd.ms-excel</param>
                <param name="contentDisposition">attachment;filename="metrics.xls"</param>
                <param name="inputName">excelFile</param>
        </result>
        <result name="error" type="flexVelocity">
                <param name="location">/WEB-INF/vm/conf/common_error.vm</param>
        </result>
</action>

Action中的主要代码:
public class ReportAction extends ForecastBaseAction {
        private static final long serialVersionUID = 12163451254776L;
        private final static Log log = LogFactory.getLog(ReportAction.class);
        private ReportService reportService;
        private List<Report> rowsData;
        private List<String> across;
        private String e;
        private InputStream excelFile;
        public String excel() throws Exception {
                try {
                        this.readData();
                        excelFile = ExcelUtil.genExcel(rowsData);
                        return SUCCESS;
                } catch (Exception e) {
                        this.e = StringUtil.generateStackTrace(e);
                        return ERROR;
                }
        }
        private List<Report> readData() throws Exception {
                String map = "Metrics.metric";
                String targetSelected = this.request.getParameter("target_selected");
                HashMap<String, Object> param = new HashMap<String, Object>();
                param.put("forecast_date", this.request.getParameter("forecast_date"));
                param.put("wid", this.request.getParameter("wid"));
                param.put("product_sort", this.request.getParameter("product_sort"));
                param.put("product_sort_level", this.request.getParameter("product_sort_level"));
                param.put("dimon", this.request.getParameter("dimon"));
                // 用户输入的target_selected中含有多个指标,以;作为分隔符
                this.across = new ArrayList<String>();
                for (String s : targetSelected.split(";")) {
                        if (!s.isEmpty()) {
                                this.across.add(s);
                                param.put(s, s);
                        }
                }
                this.rowsData = reportService.query(map, param);
                return this.rowsData;
        }

        public List<Report> getRowsData() {
                return rowsData;
        }

        public void setReportService(ReportService reportService) {
                this.reportService = reportService;
        }

        public List<String> getAcross() {
                return this.across;
        }

        public String getE() {
                return this.e;
        }

        public InputStream getExcelFile() {
                return excelFile;
        }

}



ExcelUtil工具类的主要代码,借助了poi,poi那部分代码是抄网上的,自己只是做了反射填充
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
public class ExcelUtil {
        public static ByteArrayInputStream genExcel(List<Report> rows)
                        throws Exception {
                HSSFWorkbook workbook = new HSSFWorkbook();
                HSSFSheet sheet = workbook.createSheet("sheet1");
                {
                        String[] header = { "预测日期", "比较方式", "MAPE", "Override MAPE", "CR",
                                        "Override CR" };
                        String[] data = { "date", "dimon", "mape", "omape", "cr", "ocr" };
                        // 创建表头
                        int ri = 0, ci = 0;
                        HSSFRow row = sheet.createRow(ri++);
                        HSSFCell cell = null;
                        for (ci = 0; ci < header.length; ci++) {
                                cell = row.createCell(ci);
                                cell.setCellValue(header[ci]);
                        }
                        // 创建数据
                        for (ri = 0; ri < rows.size(); ri++) {
                                row = sheet.createRow(ri + 1);
                                for (ci = 0; ci < data.length; ci++) {
                                        cell = row.createCell(ci);
                                    cell.setCellValue(rows.get(ri).getField(data[ci]).toString());
                                }
                        }
                }
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                baos.flush();
                workbook.write(baos);
                byte[] ba = baos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(ba);
                return bais;
        }
}

domain模型的主要代码
public class Report {
        private Date date;
        private String dimon;
        private float mape;
        private float omape;
        private float cr;
        private float ocr;
        //提供反射获取Report中属性值的方法getField
        public Object getField(String name) {
                try {
                        Field f = this.getClass().getDeclaredField(name);
                        Object o = f.get(this);
                        if (f.getType().equals(Date.class)) {
                                o = DateUtil.sf.format(o);
                        }
                        return o;
                } catch (Exception e) {
                        return null;
                }
        }
        //get/set......
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值