版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhuyu19911016520/article/details/88575324
1.说明:Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能,结构如下:
- HSSF - 读写 Excel XLS 格式的功能。
- XSSF - 读写 Excel OOXML XLSX 格式的功能。
- HWPF- 读写 Word DOC 格式的功能。
- XWPF- 读写 Word DOCX 格式的功能。
官网地址:http://poi.apache.org
在SpringBoot的Web项目中,导出数据到 excel、word,写好了公用导出代码,拿去使用就好
2.导出excel
要实现2个目标:1.提供公用导出方法,导出excel到指定目录,2.提供Controller接口,已文件流方式输出的客户端浏览器
- 1.添加引用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- poi 导出excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.9</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.1</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
- 2.创建Student 学生类
@Data
public class Student {
private int id;
private String name;
private int age;
private String brith;
public Student(){}
public Student(int id, String name, int age, String brith) {
this.id = id;
this.name = name;
this.age = age;
this.brith = brith;
}
/**
* @功能:手工构建一个简单格式的Excel
*/
public static List<Student> getStudents()
{
List<Student> list = new ArrayList<Student>();
Student user1 = new Student(1, "张三", 26, "1985-11-12");
Student user2 = new Student(2, "李四", 17, "1996-08-12");
Student user3 = new Student(3, "王五", 26, "1985-11-12");
list.add(user1);
list.add(user2);
list.add(user3);
return list;
}
}
- 3.创建 ExcelUtil 导出帮助类,项目中所有excel导出用此帮助类提供的方法
/**
* excel导出帮助类
* 1、用HSSFWorkbook打开或者创建“Excel文件对象”
* 2、用HSSFWorkbook对象返回或者创建Sheet对象
* 3、用Sheet对象返回行对象,用行对象得到Cell对象
* 4、对Cell对象读写。
* 5、将生成的HSSFWorkbook放入HttpServletResponse中响应到前端页面
*/
public class ExcelUtil {
public static void main(String[] args)throws Exception{
testExportExcel();
}
//测试导出excel
public static void testExportExcel(){
try {
String[] title = {"序号" , "姓名" ,"年龄" ,"生日"};
List<Student> list = Student.getStudents();
//1.通过forEach循环,生成list数组
List<String[]> excelDatas = new ArrayList<>();
list.forEach( student -> {
String[] content = new String[4];
content[0] = StrUtil.toString(student.getId());
content[1] = student.getName();
content[2] = StrUtil.toString(student.getAge());
content[3] = student.getBrith();
excelDatas.add(content);
});
//生成cexel内容,二维String数组
String[][] values = excelDatas.toArray(new String[0][title.length]);
HSSFWorkbook wb = getHSSFWorkbook("学生表一",title , values);
String targentPath = getRootPath() + "\\src\\main\\resources\\static\\poi_template\\" + DateUtil.format(new Date(),"yyyyMMddHHmmssSSS") + ".xlsx";
FileOutputStream fout = new FileOutputStream(targentPath);
wb.write(fout);
fout.close();
System.out.println("导出excel成功,文件地址:" + targentPath);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 导出Excel
* @param sheetName sheet名称
* @param title 标题
* @param values 内容
* @return
*/
public static HSSFWorkbook getHSSFWorkbook(String sheetName, String[] title, String[][] values) {
// 第一步,创建一个HSSFWorkbook,对应一个Excel文件
HSSFWorkbook wb = new HSSFWorkbook();
// 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet
HSSFSheet sheet = wb.createSheet(sheetName);
// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
HSSFRow row = sheet.createRow(0);
// 第四步,创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格式
//声明列对象
HSSFCell cell = null;
//创建标题
for (int i = 0; i < title.length; i++) {
cell = row.createCell(i);
cell.setCellValue(title[i]);
cell.setCellStyle(style);
}
//创建内容
for (int i = 0; i < values.length; i++) {
row = sheet.createRow(i + 1);
for (int j = 0; j < values[i].length; j++) {
//将内容按顺序赋给对应的列对象
cell = row.createCell(j);
cell.setCellValue(values[i][j]);
cell.setCellStyle(style);
}
}
return wb;
}
/**
* 发送响应流方法
* @param response
* @param fileName
*/
public static void setResponseStream(HttpServletResponse response, String fileName) {
try {
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Pargam", "no-cache");
response.addHeader("Cache-Control", "no-cache");
} catch (Exception e) {
new RuntimeException("导出失败,原因:" + e.getMessage());
}
}
/**
* 获取应用根目录
* @return
*/
public static String getRootPath(){
String rootPath = "";
try {
File path = new File(ResourceUtils.getURL("classpath:").getPath());
if (!path.exists()) path = new File("");
rootPath = path.getAbsolutePath();
//System.out.println("rootPath:" + rootPath);
if(rootPath.contains("\\target\\classes")){
rootPath = rootPath.replace("\\target\\classes","");
}
}catch (Exception e){
e.printStackTrace();
}
return rootPath;
}
}
- 4.实现第一个目标,在 ExcelUtil 类中,运行 main 方法,也就是运行 testExportExcel() 方法,测试是否导出了excel,可以看到 resources\static\poi_template\ 目录下有一个刚刚导出的excel了
- 5.新建个 Controller ,提供 导出接口,访问此接口时,导出excel到客户端浏览器
@RestController
@RequestMapping("excel")
public class ExcelController {
@RequestMapping("export")
public void export(HttpServletResponse response) throws IOException {
String[] title = {"序号", "姓名", "年龄", "生日"};
List<Student> list = Student.getStudents();
//2.通过响应式流,生成list数组
List<String[]> excelDatas = list.stream().map(student -> new String[]{
StrUtil.toString(student.getId()),
student.getName(),
StrUtil.toString(student.getAge()),
student.getBrith()
}).collect(Collectors.toList());
//生成excel内容,二维String数组
String[][] values = excelDatas.toArray(new String[0][title.length]);
HSSFWorkbook wb = ExcelUtil.getHSSFWorkbook("学生表一", title, values);
String wordId = DateUtil.format(new Date(), "yyyyMMddHHmmssSSS");
ExcelUtil.setResponseStream(response, "学生表" + wordId + ".xlsx");
wb.write(response.getOutputStream());
}
}
- 6.实现第二个目标,启动项目,访问 http://localhost:8080/excel/export,可看到浏览器在下载excel
注意:通过list 生成cexel内容,二维String数组,在控制器与帮助类中应用了两种不同实现,一个是传统的foreach循环,一个是stream流式
3.导出与替换 word
导出与替换功能分了两个帮助类,因篇幅原因,我这边贴出最简代码,可下载我的源码,进行了简单的封装,更通用:
-
导出:WordExportUtil,可导出 文本、图片、表格内容
-
替换:WordReplaceUtil,需提供模版word
-
1.导出,请确保D盘有 bar.png、pie.png两张图片
public class WordExportUtil{
public static void main(String[] args) throws Exception {
XWPFDocument doc = new XWPFDocument();
XWPFParagraph para;
XWPFRun run;
//添加文本
String content =" 额尔古纳河在1689年的《中俄尼布楚条约》中成为中国和俄罗斯的界河,额尔古纳河上游称海拉尔河,源于大兴安岭西侧,西流至阿该巴图山脚, 折而北行始称额尔古纳河。额尔古纳河在黑龙江省漠河县以西的内蒙古自治区额尔古纳右旗的恩和哈达附近与流经俄罗斯境内的石勒喀河汇合后始称黑龙江。沿额尔古纳河沿岸地区土地肥沃,森林茂密,水草丰美, 鱼类品种很多,动植物资源丰富,宜农宜木,是人类理想的天堂。";
para = doc.createParagraph();
para.setAlignment(ParagraphAlignment.LEFT);//设置左对齐
run = para.createRun();
run.setFontFamily("仿宋");
run.setFontSize(13);
run.setText(content);
doc.createParagraph();
//添加图片
String[] imgs = {"D:\\bar.png","D:\\pie.png"};
for(int i=0;i<imgs.length;i++){
para = doc.createParagraph();
para.setAlignment(ParagraphAlignment.CENTER);//设置左对齐
run = para.createRun();
InputStream input = new FileInputStream(imgs[i]);
run.addPicture(input, XWPFDocument.PICTURE_TYPE_JPEG,
imgs[i], Units.toEMU(350), Units.toEMU(170));
para = doc.createParagraph();
para.setAlignment(ParagraphAlignment.CENTER);//设置左对齐
run = para.createRun();
run.setFontFamily("仿宋");
run.setFontSize(11);
run.setText(imgs[i]);
}
doc.createParagraph();
//添加表格
XWPFTable table = doc.createTable(2,3);
table.setCellMargins(3, 5, 3, 5);
String[] title = new String[]{"境内河流","境外河流","合计"};
String[] value = new String[]{"1","2","3"};
XWPFTableRow row;
XWPFTableCell cell;
CTTcPr cellPr;
for(int j=0;j<2;j++){
row = table.getRow(j);
row.setHeight(400);
for(int i=0;i<title.length;i++){
cell = row.getCell(i);
cellPr = cell.getCTTc().addNewTcPr();
cellPr.addNewTcW().setW(BigInteger.valueOf(3000));
para = cell.getParagraphs().get(0);
para.setAlignment(ParagraphAlignment.CENTER);
run = para.createRun();
run.setFontFamily("仿宋");
run.setFontSize(11);
if(j==0){//标题
run.setBold(true);
run.setText(title[i]);
}
else{
run.setText(value[i]);
}
}
}
String path = "D:\\test.doc";
OutputStream os = new FileOutputStream(path);
doc.write(os);
if(os!=null){
try{
os.close();
System.out.println("文件已输出!");
}
catch(IOException e){
e.printStackTrace();
}
}
}
}
- 2.替换 ,需要准备好要替换的word模版,再运行下面的测试方法
public class WordReplaceUtil {
public static void main(String[] args) throws Exception, IOException {
testReplaceAndSaveDoc();
}
//测试替换word内容
private static void testReplaceAndSaveDoc(){
//替换 [sum] 标签 与 习近平 字样
Map<String, Object> map=new HashMap<String, Object>();
map.put("[sum]", "长沙");
map.put("习近平", "朱宇");
String wordId = DateUtil.format(new Date(),"yyyyMMddHHmmssSSS");
String sourcePath = CommonUtil.getRootPath() + "\\src\\main\\resources\\static\\poi_template\\replace_word_template.docx";
String targetPath = CommonUtil.getRootPath() + "\\src\\main\\resources\\static\\poi_template\\" + wordId + ".docx";
try {
replaceAndSaveDoc(sourcePath, map , targetPath);
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* 读取word模板替换变量,并根据目标路径生成新的word文档
* @param fis 模版文件流
* @param param 要替换的键值对
* @param targetPath 生成文档目标路径
*/
public static void replaceAndSaveDoc(InputStream fis, Map<String, Object> param , String targetPath) throws FileNotFoundException{
if(fis == null){
throw new RuntimeException("请传入源文件");
}
XWPFDocument doc = replaceDoc(fis, param);
outPutWord(doc , targetPath);
}
/**
* 读取word模板替换变量,并根据目标路径生成新的word文档
* @param sourcePath 文档源路径
* @param param 要替换的键值对
* @param targetPath 生成文档目标路径
*/
public static void replaceAndSaveDoc(String sourcePath, Map<String, Object> param , String targetPath) throws FileNotFoundException{
// 读取word模板
File f = new File(sourcePath);
if(!f.exists()){
throw new RuntimeException("未读取到源文件");
}
InputStream fis = new FileInputStream(f);
XWPFDocument doc = replaceDoc(fis, param);
outPutWord(doc , targetPath);
}
/**
* 读取word模板并替换变量
* @param fis 模版文件流
* @param param 要替换的键值对
* @return
*/
private static XWPFDocument replaceDoc(InputStream fis, Map<String, Object> param) {
try {
XWPFDocument doc = new XWPFDocument(fis);
//处理段落
List<XWPFParagraph> paragraphList = doc.getParagraphs();
processParagraph(paragraphList,doc,param);
//处理表格
Iterator<XWPFTable> it = doc.getTablesIterator();
while (it.hasNext()) {
XWPFTable table = it.next();
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
processParagraph(paragraphListTable, doc, param);
}
}
}
return doc;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 替换文字
* @param paragraphList
* @param doc
* @param param
*/
private static void processParagraph(List<XWPFParagraph> paragraphList, XWPFDocument doc,Map<String, Object> param){
if(paragraphList != null && paragraphList.size() > 0){
for(XWPFParagraph paragraph:paragraphList){
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if(text != null){
boolean isSetText = false;
for (Map.Entry<String, Object> entry : param.entrySet()) {
String key = entry.getKey();
if(text.indexOf(key) != -1){
isSetText = true;
Object value = entry.getValue();
if (value instanceof String) {//文本替换
text = text.replace(key, value.toString());
//System.out.println(text);//放开注释可打印读取到的word内容
}
else{
text = text.replace(key, "");
}
}
}
if(isSetText){
run.setText(text,0);
}
}
}
}
}
}
/**
* 输出word到目标路径
* @param doc 文档对象
* @param targetPath 目标路径
* @throws FileNotFoundException
*/
private static void outPutWord(XWPFDocument doc , String targetPath) throws FileNotFoundException{
try {
if (doc != null) {
OutputStream os = new FileOutputStream(targetPath);
doc.write(os);
os.close();
System.out.println("已替换word文件,文件地址:" + targetPath);
}
}catch (IOException e){
e.printStackTrace();
}
}
执行测试方法后,可看到项目下多了个word,打开word,标题被我替换了,习近平替换成朱宇,“习总我只是做个测试,勿怪”
需要注意的是,上面的导出word帮助类,我只提供了导出的最简实现,我在最简实现上再封装了一层,如果导出的图片不能显示,请下载项目源码
下载源码后,可直接运行各个帮助类的 main 方法进行测试
https://gitee.com/zhuyu1991/spring-cloud/tree/master/poi_demo