一、先在pom.xml中引入poi依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
关于办公文档插件使用的基本思想:把办公文档的所有元素封装成普通的Java类,程序员通过操作这些类达到操作办公文档的目的。
文件——HSSFWorkbook
页——HSSFSheet
行——HSSFRow
列——HSSFCell
样式——HSSFCellStyle
二、JSP页面
给导出按钮设置id="exportActivityChooseByIdBtn",添加单击事件,发送同步请求
//给“选择导出”按钮添加单击事件
$("#exportActivityChooseByIdBtn").click(function () {
var checkedIds = $("#tBody input:checked")//父标签tBody下的被选中的input
if(checkedIds.size()==0){
alert("请选择要导出的活动");
return;
}
if(window.confirm("您确定要导出这"+checkedIds.size()+"条活动吗?")){
var ids=[];
$.each(checkedIds,function () {
ids.push(this.value);//this.value就是被选中的id值
});
window.location.href="workbench/activity/exportChooseActivitysByIds.do?ids="+ids;//注意传参格式
}
});
三、Controller层,调用service,根据id查询活动后使用返回的activitychooseList导出到excel
@RequestMapping("/workbench/activity/exportChooseActivitysByIds.do")
public void exportChooseActivitysByIds(String[] ids, HttpServletResponse response) throws Exception{
//调用service返回activitychooseList
List<Activity> activitychooseList = activityService.querychooseActivityByIds(ids);
//用apache的poi创建excel文件,并把activityList写入excel文件中
//创建HSSFWorkbook对象,对应一个excel文件
HSSFWorkbook wb = new HSSFWorkbook();
//使用wb创建HSSFSheet对象,对应wb文件中的一页
HSSFSheet sheet = wb.createSheet("市场活动列表");//设置页名
//使用sheet创建HSSFRow对象,对应sheet中的一行
HSSFRow row = sheet.createRow(0);//索引为0就是第一行
//使用row创建HSSFCell对象,对应row中的列
HSSFCell cell = row.createCell(0);//索引为0就是第一列
//往列里设置值
cell.setCellValue("ID");
cell = row.createCell(1);
cell.setCellValue("所有者");
cell = row.createCell(2);
cell.setCellValue("活动名称");
cell = row.createCell(3);
cell.setCellValue("开始日期");
cell = row.createCell(4);
cell.setCellValue("结束日期");
cell = row.createCell(5);
cell.setCellValue("成本");
cell = row.createCell(6);
cell.setCellValue("描述");
cell = row.createCell(7);
cell.setCellValue("创建时间");
cell = row.createCell(8);
cell.setCellValue("创建者");
cell = row.createCell(9);
cell.setCellValue("修改时间");
cell = row.createCell(10);
cell.setCellValue("修改者");
//先判断list有没有数据
if(activitychooseList!=null&&activitychooseList.size()>0){//需要首先判断是否有地址,null就代表没有地址则未创建对象,如果已经创建对象,我们就需要判断该对象中是否有值,这个时候就使用空串来进行判断。
Activity activity=null;
for(int i=0;i<activitychooseList.size();i++){
// Activity activity = activityList.get(i); activity拿到外面定义就不用每次循环都创建,效率更高
activity = activitychooseList.get(i);
//每遍历出一个activity,生成一行
row=sheet.createRow(i+1);
cell = row.createCell(0);
cell.setCellValue(activity.getId());
cell = row.createCell(1);
cell.setCellValue(activity.getOwner());
cell = row.createCell(2);
cell.setCellValue(activity.getName());
cell = row.createCell(3);
cell.setCellValue(activity.getStartDate());
cell = row.createCell(4);
cell.setCellValue(activity.getEndDate());
cell = row.createCell(5);
cell.setCellValue(activity.getCost());
cell = row.createCell(6);
cell.setCellValue(activity.getDescription());
cell = row.createCell(7);
cell.setCellValue(activity.getCreateTime());
cell = row.createCell(8);
cell.setCellValue(activity.getCreateBy());
cell = row.createCell(9);
cell.setCellValue(activity.getEditTime());
cell = row.createCell(10);
cell.setCellValue(activity.getEditBy());
}
}
//改进前,这里说明了很多繁杂步骤,第六点改进后的可以直接替换下面全部
OutputStream os = new FileOutputStream("E:\\serverDir\\activityList.xls");
wb.write(os);//wb.write(os)表示你要吧它生成到哪个文件。从内存写到磁盘。内存访问磁盘效率是很低的,要建立链接,占内存,占cpu,不亚于访问一次数据库
os.close();
wb.close();
//把生成的excel文件下载到客户端
//设置响应类型 application/octet-stream表示excel文件
response.setContentType("application/octet-stream;charset=UTF-8");
//设置响应头 Content-Disposition打开方式 attachment以附件的形式
response.addHeader("Content-Disposition","attachment;filename=AllactivityList.xls");
OutputStream out = response.getOutputStream();
InputStream is = new FileInputStream("E:\\serverDir\\activityList.xls");//从磁盘读到内存,要建立链接,占内存,占cpu,不亚于访问一次数据库
//缓存区,以一个字节数组为单位读更快
byte[] buff = new byte[256];
int len=0; //实际读取个数,等于-1时说明读完了
while((len=is.read(buff))!=-1){
out.write(buff,0,len);
}
is.close();
out.flush();//out由response生成的,tomcat会自己关闭,不能自己关闭,不flush可能会数据丢失,因为可能还在缓冲区里
}
记得配置扫描Controller
四、Service和实现类
public interface ActivityService {
List<Activity> querychooseActivityByIds(String[] ids);
}
@Service("activityService")
public class ActivityServiceImpl implements ActivityService{
@Autowired
ActivityMapper activityMapper;
@Override
public List<Activity> querychooseActivityByIds(String[] ids) {
return activityMapper.selectchooseActivityByIds(ids);
}
}
记得配置扫描Service
五、Dao和mapper
public interface ActivityMapper {
//根据所选id查询活动
List<Activity> selectchooseActivityByIds(String[] ids);
}
<!--//根据所选ids查询活动-->
<select id="selectchooseActivityByIds" parameterType="String" resultMap="BaseResultMap">
select a.id , u1.name as owner, a.name, a.start_date, a.end_date, a.cost, a.description, a.create_time,
u2.name as create_by, a.edit_time, u3.name as edit_by
from tbl_activity a
join tbl_user u1 on a.owner = u1.id
join tbl_user u2 on a.create_by = u2.id
left join tbl_user u3 on a.edit_by =u3.id
where a.id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
order by a.create_time desc
</select>
记得配置mapper注解扫描器
六、改进
先写到磁盘再读取磁盘效率太低了,正好workbook对象里有个方法wb.write(out) ,把workbook对象里的数据输出到outputStream对象out里,直接让它输出到浏览器。比起上面效率更高、代码更简洁。
//设置响应类型 application/octet-stream表示excel文件
response.setContentType("application/octet-stream;charset=UTF-8");
//设置响应头 Content-Disposition打开方式 attachment以附件的形式
response.addHeader("Content-Disposition","attachment;filename=AllactivityList.xls");
OutputStream out = response.getOutputStream();
wb.write(out);
wb.close();
out.flush();//out由response生成的,tomcat会自己关闭,不能自己关闭,不flush可能会数据丢失
七、总结
1、导出功能不能使用异步请求,这样下载框弹不出来,只能使用同步请求(三种:地址栏、超链接、表单)
2、这里发送请求是用地址栏,特别注意地址栏的传参格式
3、var ids=[]; 在JS中创建数组必须使用这个形式,不能 var ids = new Array(); 我就是这里找bug找很久,也不知道为什么,有懂的大哥能说一下吗?
觉得写得不错就点个赞吧😋