用jxl导出数据,1万条以上会内存溢出,怎么优化代码?

lvzhuze 2011-07-22 05:16:58
哪位高手指点一二,谢谢!

@RequestMapping(value = "/exportperson.do")
public @ResponseBody void writeExcel(PersonView personView,HttpServletRequest request,HttpServletResponse response,@ModelAttribute("currSysUser") SysUser sysUser) {
List<Person> lists = personService.personSearch(personView, sysUser);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String fileName = sdf.format(new Date());
fileName = fileName + ".xls" ;
ExtJsonForm ejf = new ExtJsonForm();
try {
OutputStream out = null ;
out = response.getOutputStream();
WritableWorkbook wwb = Workbook.createWorkbook(out);
WritableSheet ws = wwb.createSheet("人员信息", 0);
ws.setColumnView(4, 22) ; // 设置身份证的列宽
ws.setColumnView(6, 20) ; // 设置籍贯的列宽
ws.setColumnView(9, 15) ; // 设置日期列宽
ws.setColumnView(10, 15) ;
ws.setColumnView(11, 15) ;
ws.setColumnView(12, 15) ;
/**
* 只能通过API提供的工厂方法来创建Workbook,而不能使用WritableWorkbook的构造函数,
* 因为类WritableWorkbook的构造函数为protected类型
* method(1)直接从目标文件中读取WritableWorkbook wwb =
* Workbook.createWorkbook(new File(targetfile)); method(2)如下实例所示
* 将WritableWorkbook直接写入到输出流
*/
// 创建Excel工作表 指定名称和位置
ws=getWritableSheet(ws,lists);
/*// 写入工作表
response.setHeader("Content-disposition","attachment;" +
"filename="+ new String("人员数据".getBytes("GBK"), "ISO_8859_1") +".xls");
response.setContentType("application/vnd.ms-excel"); */
response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setDateHeader("Expires", (System.currentTimeMillis() + 10000));
wwb.write();
wwb.close();
out.close();
} catch (Exception e6) {
ejf.setSuccess(false);
e6.printStackTrace();
}
}

public WritableSheet getWritableSheet( WritableSheet ws,List<Person> lists){
int index = 0;

String header[] = {" 姓名","所在单位","所在处室","性别","身份证号码","民族","籍贯",
"个人身份","任职名称","出生日期","任职日期","工作时间","入党(团)时间",
"政治面貌","学历","学位","职称","职务级别","职务类别","享受职务待遇","备注"} ;
// **************往工作表中添加数据*****************
// 1.添加Label对象 Lable(i,j,str) 第一个参数是列,第二个参数是行列,第三个参数是内容
try {
for(int i=0;i<header.length;i++){
Label headerLabel = new Label(i, 0, header[i]);
ws.addCell(headerLabel);
}
for(int i=0;i<lists.size();i++){
Label name = new Label(index,i+1,lists.get(i).getName()) ;
ws.addCell(name);
// 姓名
index++ ;
Label unitName = new Label(index,i+1,lists.get(i).getUnit().getUnitName()) ;
ws.addCell(unitName); // 所在单位
index++;
if(lists.get(i).getDept()!=null){
Label dept = new Label(index,i+1,lists.get(i).getDept().getUnitName()) ;
if(lists.get(i).getDept().getUnitName()!=null)
ws.addCell(dept); // 所在处室 ---unitName
}
index++ ;
Label sexValue = new Label(index,i+1,lists.get(i).getSexValue()) ;
ws.addCell(sexValue); // 性别
index++ ;
Label idcard = new Label(index,i+1,lists.get(i).getIdcard()) ;
ws.addCell(idcard); // 身份证号码
index++;
Label folkCodeValue = new Label(index,i+1,lists.get(i).getFolkCodeValue()) ;
ws.addCell(folkCodeValue); // 民族
index++ ;
Label nativeCodeValue = new Label(index,i+1,lists.get(i).getNativeCodeValue()) ;
ws.addCell(nativeCodeValue); // 籍贯
index++;
Label personIdentity = new Label(index,i+1,lists.get(i).getPersonIdentity()) ;
ws.addCell(personIdentity); // 个人身份
index++ ;
Label dutyTitle = new Label(index,i+1,lists.get(i).getDutyTitle()) ;
ws.addCell(dutyTitle); // 任职名称
// 添加带有formatting的DateFormat对象
DateFormat df = new DateFormat("yyyy年MM月dd日");
WritableCellFormat wcfDF = new WritableCellFormat(df);
index++ ;
if(lists.get(i).getBirthday()!=null){
DateTime birthday = new DateTime(index, i+1, lists.get(i).getBirthday(), wcfDF);
if(birthday!=null)
ws.addCell(birthday); // 出生日期
}
index++;
if(lists.get(i).getInDutyDate()!=null){
DateTime inDutyDate = new DateTime(index, i+1, lists.get(i).getInDutyDate(), wcfDF);
if(inDutyDate!=null)
ws.addCell(inDutyDate); // 在职日期
}
index++ ;
if(lists.get(i).getWorkDay()!=null){
DateTime workDay = new DateTime(index, i+1, lists.get(i).getWorkDay(), wcfDF);
if(workDay!=null)
ws.addCell(workDay) ; // 工作时间
}
index++ ;
if(lists.get(i).getKaiserdomDate()!=null){
DateTime kaiserdomDate = new DateTime(index, i+1, lists.get(i).getKaiserdomDate(), wcfDF);
if(kaiserdomDate!=null)
ws.addCell(kaiserdomDate) ; // 入党(团)时间
}
index++ ;
if(lists.get(i).getKaiserdomCodeValue()!=null){
Label kaiserdomCodeValue = new Label(index,i+1,lists.get(i).getKaiserdomCodeValue()) ;
ws.addCell(kaiserdomCodeValue); // 政治面貌
}
index++ ;
if(lists.get(i).getStudyCodeValue()!=null){
Label studyCodeValue = new Label(index,i+1,lists.get(i).getStudyCodeValue()) ;
ws.addCell(studyCodeValue); // 学历
}
index++ ;
if(lists.get(i).getDegreeCodeValue()!=null){
Label degreeCodeValue = new Label(index,i+1,lists.get(i).getDegreeCodeValue()) ;
ws.addCell(degreeCodeValue); // 学位
}
index++ ;
if(lists.get(i).getZhiChengCodeValue()!=null){
Label zhiChengCodeValue = new Label(index,i+1,lists.get(i).getZhiChengCodeValue()) ;
ws.addCell(zhiChengCodeValue); //职称
}
index++;
if(lists.get(i).getDutyLevelValue()!=null){
Label dutyLevelValue = new Label(index,i+1,lists.get(i).getDutyLevelValue()) ;
ws.addCell(dutyLevelValue); // 职务级别
}
index++ ;
if(lists.get(i).getPersonSortCodeValue()!=null){
Label personSortCodeValue = new Label(index,i+1,lists.get(i).getPersonSortCodeValue()) ;
ws.addCell(personSortCodeValue); // 职务类别
}
index++ ;
if(lists.get(i).getEnjoyDealLevelValue()!=null){
Label enjoyDealLevelValue = new Label(index,i+1,lists.get(i).getEnjoyDealLevelValue()) ;
ws.addCell(enjoyDealLevelValue); // 享受职务待遇
}

index++ ;
if(lists.get(i).getMemo()!=null){
Label memo = new Label(index,i+1,lists.get(i).getMemo()) ;
ws.addCell(memo); // 备注
}
index = 0;
}
} catch (RowsExceededException e1) {
e1.printStackTrace();
} catch (WriteException e2) {
e2.printStackTrace();
} catch(Exception e3){
e3.printStackTrace() ;
}
return ws;
}
...全文
1564 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
kafen88 2011-08-01
  • 打赏
  • 举报
回复
这还用说么。。当然是分页(分批)读取了。。。数据量大,有谁一次性读呀。。
KingViker 2011-07-29
  • 打赏
  • 举报
回复
我现在需要实现的功能是:
前台是用Ext js,用Excel把查询结果从服务器导出到客户端,数据有几万条也有可能几十万条?现在的实现使用上面的代码完成的,效果不理想,有没有更好的办法来解决这个问题


我感觉你可以这样 首先你可以在查询数据库的时候判断一下总条数 一次性指导处5000条(假设) 大于5000的就分次呗 来个循环就可以了第一次创建excel 然后就全部是追加就可以了。这只是我的想法性能上应该也可以优化 可以保持文件流开启 全部导出完在关闭,
sjlzcj 2011-07-27
  • 打赏
  • 举报
回复
首先 从数据库中读取 还是建议分页处理 毕竟1W条怎么的也得几M大小了吧

分页读取数据库中的数据 将个页面生成一个临时的XLS文件 这样分10页 就产生了10个结构相同的 XLS 文件

然后 参考
http://www.iteye.com/topic/240053
给出的方法 将10个临时的XLS文件合并

将合并后的XLS文件提供给前台下载
最后可以根据需要把临时文件删除



再提一下啊 如果 确定只有1W行 并且以后数据量也不会增加 你不妨修改一下JVM大小 不过修改JVM 不是根本解决问题的方法 但却是最简单的方法
lvzhuze 2011-07-27
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 lvzhuze 的回复:]
引用 17 楼 jiannye 的回复:
引用 16 楼 lvzhuze 的回复:
引用 15 楼 humanity 的回复:
似乎 M$ Excel 一个 Sheet 只能装最多 65535 条记录。咋办?分页么?

如果非得分页不如早点分页,还能减少内存使用。



这个分页导出有效果吗?数据都是要全部导出的,一次性跟分页有什么区别呢?如果分页,则怎么实现?

分页不就是……
[/Quote]



我现在需要实现的功能是:
前台是用Ext js,用Excel把查询结果从服务器导出到客户端,数据有几万条也有可能几十万条?现在的实现使用上面的代码完成的,效果不理想,有没有更好的办法来解决这个问题?
lvzhuze 2011-07-27
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 lvzhuze 的回复:]
引用 17 楼 jiannye 的回复:
引用 16 楼 lvzhuze 的回复:
引用 15 楼 humanity 的回复:
似乎 M$ Excel 一个 Sheet 只能装最多 65535 条记录。咋办?分页么?

如果非得分页不如早点分页,还能减少内存使用。



这个分页导出有效果吗?数据都是要全部导出的,一次性跟分页有什么区别呢?如果分页,则怎么实现?

分页不就是……
[/Quote]



lvzhuze 2011-07-27
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 jiannye 的回复:]
引用 16 楼 lvzhuze 的回复:
引用 15 楼 humanity 的回复:
似乎 M$ Excel 一个 Sheet 只能装最多 65535 条记录。咋办?分页么?

如果非得分页不如早点分页,还能减少内存使用。



这个分页导出有效果吗?数据都是要全部导出的,一次性跟分页有什么区别呢?如果分页,则怎么实现?

分页不就是查询语句加个分页么。。 你页面一个页面难道会……
[/Quote]


哦!
导出到Excel中的数据有几万条,导出数据通过后台代码导出的,这跟分页查询有关系?
jiannye 2011-07-26
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 lvzhuze 的回复:]
引用 15 楼 humanity 的回复:
似乎 M$ Excel 一个 Sheet 只能装最多 65535 条记录。咋办?分页么?

如果非得分页不如早点分页,还能减少内存使用。



这个分页导出有效果吗?数据都是要全部导出的,一次性跟分页有什么区别呢?如果分页,则怎么实现?
[/Quote]
分页不就是查询语句加个分页么。。 你页面一个页面难道会呈现1w条数据么?
lvzhuze 2011-07-26
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 humanity 的回复:]
似乎 M$ Excel 一个 Sheet 只能装最多 65535 条记录。咋办?分页么?

如果非得分页不如早点分页,还能减少内存使用。
[/Quote]


这个分页导出有效果吗?数据都是要全部导出的,一次性跟分页有什么区别呢?如果分页,则怎么实现?
humanity 2011-07-26
  • 打赏
  • 举报
回复
似乎 M$ Excel 一个 Sheet 只能装最多 65535 条记录。咋办?分页么?

如果非得分页不如早点分页,还能减少内存使用。
RothCold 2011-07-26
  • 打赏
  • 举报
回复
分页读取然后append到xls里面...理论上大概就是这样了.
ch_f001 2011-07-24
  • 打赏
  • 举报
回复
建议每1000条读取一次
yu1ei 2011-07-24
  • 打赏
  • 举报
回复
使用net.sf.jxls.transformer.XLSTransformer导出,模板即可,不用在java代码里定义列,写单元格。

public class ExportExcel {

/** 模板放置的路径 */
public static final String TEMPLATE_PATH = "/WEB-INF/excelTemplate/";

/** 导出的Excel临时放置的路径 */
public static final String EXCEL_TEMP_PATH = "/WEB-INF/excelTemplate/temp/";

// 获得导出文件的模板。
private static String getTemplateFile(String fileName) {
// 获得当前应用的全路径,就是"/web"
String strBasePatch = XXX.getWebPath();
// 获得模板文件的全路径
fileName = strBasePatch + TEMPLATE_PATH + fileName;
return fileName;
}

// 获得导出文件。
private static String getExportFile(String fileName) {
// 对特殊字符进行转义,以免出现文件路径错误
fileName = fileName.replaceAll("[\\\\/*?<>:\"|]", "-");
// 获得当前应用的全路径,就是"/web"
String strBasePatch = Toolkit.getWebPath();
// 根据导出文件名获得临时文件名,这样做为了防止同时请求时出现冲突,然后获得输出的Excel文件的全路径
fileName = strBasePatch + EXCEL_TEMP_PATH + fileName.replace('.', '_')
+ System.currentTimeMillis() + ".xls";
return fileName;
}

/**
*
* @param request
* @param response
* @param templateFileName-模板文件的名称
* @param exportFileName-要导出的Excel文件的名称
* @throws Exception
*/
public static void exportExcel(Map beans, HttpServletResponse response,
String templateFileName, String exportFileName) {
templateFileName = getTemplateFile(templateFileName);
String strPhysicalFileName = getExportFile(exportFileName);

XLSTransformer transformer = new XLSTransformer();
File src = null;
InputStream pi = null;
ServletOutputStream sos = null;
try {
transformer.transformXLS(templateFileName, beans,
strPhysicalFileName);
// 再使用流的方式来读取这个文件,我觉得这个地方应该有一个方法能够直接生成流,但是没有找到。
response.reset();
response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition", PubFunctions
.unicodeToGB("attachment;filename=\"" + exportFileName
+ "\""));
sos = response.getOutputStream();
src = new File(strPhysicalFileName);
if (!src.exists() || !src.isFile()) {
throw new Exception(
"No Such Source File" + strPhysicalFileName, null);
}
if (!src.canRead()) {
throw new Exception("SourceFile :" + strPhysicalFileName
+ "is unreadable", null);
}
pi = new FileInputStream(src);
byte[] blobbytes = new byte[10240];
int bytesRead = 0;
while ((bytesRead = pi.read(blobbytes)) != -1) {
sos.write(blobbytes, 0, bytesRead);
}
sos.flush();
} catch (Exception e) {

} finally {
try {
pi.close();
src.delete();
} catch (Exception e) {
}
}
}


需要引入四个jar:
commons-jexl-1.0.jar\jxl.jar\jxls-core-0.9.5.jar\jxls-reader-0.9.5.jar
fnzh0003 2011-07-23
  • 打赏
  • 举报
回复
一万条记录就这样,看来这个框架有问题.
lvzhuze 2011-07-22
  • 打赏
  • 举报
回复
(不好意思 上面的问题描述的不是很清楚)
问题补充:

我实现的功能是:把查询的数据导出,通过response下载到本地,数据必须是一次输出来了吧?数据导出到Excel,只能一次性输出吧?
  ws=getWritableSheet(ws,lists);
/*// 写入工作表
response.setHeader("Content-disposition","attachment;" +
"filename="+ new String("人员数据".getBytes("GBK"), "ISO_8859_1") +".xls");
response.setContentType("application/vnd.ms-excel"); */
response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setDateHeader("Expires", (System.currentTimeMillis() + 10000));
wwb.write();
wwb.close();
out.close();
xianaofei 2011-07-22
  • 打赏
  • 举报
回复
1 你调整一下JVM内存
2 你可以启用多个线程生产多个文件输出,最后合并成一个文件
wula0010 2011-07-22
  • 打赏
  • 举报
回复
1万条记录不算多,10万条应该没什么问题,再多就要增加jvm的启动的缓存了。
wula0010 2011-07-22
  • 打赏
  • 举报
回复
你的错误原因是Warning: Maximum number of format records exceeded. Using default format.

这个不是内存溢出。
DateFormat df = new DateFormat("yyyy年MM月dd日");
WritableCellFormat wcfDF = new WritableCellFormat(df);
是你的格式定义的太多了,把这个格式定义放到循环的外面,我以前碰到过,这样就好了,.......
zl3450341 2011-07-22
  • 打赏
  • 举报
回复
哎。。今天真的是不在状态。。我还以为你说读取Excel中1w数据内存溢出了。。。

用楼上的分页读取吧,没有人一次性读1W条的。
zn85600301 2011-07-22
  • 打赏
  • 举报
回复
分配读取吧 这么多性能有问题
HeiBoyYang 2011-07-22
  • 打赏
  • 举报
回复
表中建立索引
不然效率低
导出很慢
页面很卡
加载更多回复(3)

67,512

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧