項(xiàng)目中經(jīng)常會(huì)有列表查詢(xún),然后導(dǎo)出excel的功能,以下是其中一種方法,簡(jiǎn)單寫(xiě)個(gè)Demo
,先看項(xiàng)目結(jié)構(gòu):
- pom.xml
<properties>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
</properties>
<dependencies>
<!--注意:由于 spring-boot-starter-web 默認(rèn)替我們引入了核心啟動(dòng)器 spring-boot-starter,
因此,當(dāng) Spring Boot 項(xiàng)目中的 pom.xml 引入了 spring-boot-starter-web 的依賴(lài)后,
就無(wú)須在引入 spring-boot-starter 核心啟動(dòng)器的依賴(lài)了-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<!--整合mytais-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<!--excel-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!--excel2003版本要引入此包-->
<!-- <dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>-->
</dependencies>
其他依賴(lài)就不提了,主要就只這個(gè)依賴(lài)org.apache.poi.poi-ooxml
說(shuō)明:
poi是Apache旗下的一個(gè)開(kāi)源項(xiàng)目,由Apache官方維護(hù),poi有兩個(gè)不同的jar包,分別是處理excel2003和excel2007+的,對(duì)應(yīng)的是poi和poi-ooxml。畢竟poi-ooxml是poi的升級(jí)版本,處理的單頁(yè)數(shù)據(jù)量也是百萬(wàn)級(jí)別的,所以我們選擇的也是poi-ooxml。
- application.yml
spring:
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.233.136:3306/mydata?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
username: root
password: 123456
mybatis:
mapperLocations: classpath*:mapper/*Mapper.xml
# 打印sql語(yǔ)句
logging:
level:
com.meng.user: debug
- Excel2Application
@SpringBootApplication
@MapperScan("com.meng.dao")
public class Excel2Application {
public static void main(String[] args) {
SpringApplication.run(Excel2Application.class , args);
}
}
- entity
@Data
public class BaiDuResult {
private Long id;
/**
*標(biāo)題
*/
private String title;
/**
*內(nèi)容
*/
private String content;
/**
*內(nèi)容來(lái)源
*/
private String sourceUrl;
/**
*封面圖片
*/
private String imgUrl;
/**
*創(chuàng)建時(shí)間
*/
private Date createTime;
/**
*更新時(shí)間
*/
private Date updateTime;
/**
*是否刪除
*/
private Byte delFlag;
}
- dao
@Repository
public interface BaiDuResultDao {
List<BaiDuResult> findAllPage(@Param("start") int start , @Param("pageSize")int pageSize);
- BaiDuResultMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.meng.dao.BaiDuResultDao" >
<resultMap id="BaseResultMap" type="com.meng.entity.BaiDuResult" >
<id column="id" property="id" jdbcType="BIGINT" />
<result column="title" property="title" jdbcType="VARCHAR" />
<result column="content" property="content" jdbcType="VARCHAR" />
<result column="source_url" property="sourceUrl" jdbcType="VARCHAR" />
<result column="img_url" property="imgUrl" jdbcType="VARCHAR" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
<result column="del_flag" property="delFlag" jdbcType="TINYINT" />
</resultMap>
<sql id="Base_Column_List" >
id, title, content, source_url, img_url, create_time, update_time, del_flag
</sql>
<select id="findAllPage" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from result limit #{start} , #{pageSize}
</select>
- service
@Service
public class ResultService {
@Autowired
private BaiDuResultDao dao;
public Workbook exportToExcel(){
//這是表頭
String[] arr = {"ID","標(biāo)題","內(nèi)容","內(nèi)容來(lái)源","封面圖片","創(chuàng)建時(shí)間","更新時(shí)間","是否刪除"};
//這是具體數(shù)據(jù)
List<BaiDuResult> list = dao.findAllPage(0, 1000);
Workbook workbook = ExcelUtil.writeToExcelByList(arr, list , BaiDuResult.class);
return workbook;
}
}
- controller
@RestController
public class ResultController {
@Autowired
private ResultService resultService;
@GetMapping("/export")
public void exportResult(HttpServletResponse response) throws IOException {
Workbook wb = resultService.exportToExcel();
OutputStream output = response.getOutputStream();
String fileName = "結(jié)果表.xlsx";
try {
fileName = URLEncoder.encode(fileName, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ";" + "filename*=utf-8''" + fileName);
wb.write(output);
output.close();
}
}
- ExcelUtils
以上都不重要,重要的是這個(gè)工具類(lèi)的方法
public class ExcelUtil{
public static <T> Workbook writeToExcelByList(String[] array, List<T> list , Class<T> clazz) {
//創(chuàng)建工作薄
Workbook wb = new XSSFWorkbook();
//標(biāo)題和頁(yè)碼
CellStyle titleStyle = wb.createCellStyle();
// 設(shè)置單元格對(duì)齊方式,水平居左
titleStyle.setAlignment(HorizontalAlignment.LEFT);
//單元格邊框
titleStyle.setBorderTop(BorderStyle.THIN);
titleStyle.setBorderLeft(BorderStyle.THIN);
titleStyle.setBorderRight(BorderStyle.THIN);
titleStyle.setBorderBottom(BorderStyle.THIN);
// 設(shè)置字體樣式
Font titleFont = wb.createFont();
// 字體高度
titleFont.setFontHeightInPoints((short) 12);
// 字體樣式
titleFont.setFontName("黑體");
titleStyle.setFont(titleFont);
//創(chuàng)建sheet
Sheet sheet = wb.createSheet("接入詳情");
// 自動(dòng)設(shè)置寬度
sheet.autoSizeColumn(0);
// 在sheet中添加標(biāo)題行// 行數(shù)從0開(kāi)始
Row row = sheet.createRow(0);
for (int i = 0; i < array.length; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(array[i]);
cell.setCellStyle(titleStyle);
}
// 數(shù)據(jù)樣式 因?yàn)闃?biāo)題和數(shù)據(jù)樣式不同 需要分開(kāi)設(shè)置 不然會(huì)覆蓋
CellStyle dataStyle = wb.createCellStyle();
//單元格邊框
dataStyle.setBorderTop(BorderStyle.THIN);
dataStyle.setBorderLeft(BorderStyle.THIN);
dataStyle.setBorderRight(BorderStyle.THIN);
dataStyle.setBorderBottom(BorderStyle.THIN);
// 設(shè)置居中樣式,水平居中
dataStyle.setAlignment(HorizontalAlignment.CENTER);
//數(shù)據(jù)從序號(hào)1開(kāi)始
try {
int index = 1;
Field[] fields = clazz.getDeclaredFields();
for (T t : list) {
// 默認(rèn)的行數(shù)從0開(kāi)始,為了統(tǒng)一格式設(shè)置從1開(kāi)始,就是從excel的第二行開(kāi)始
row = sheet.createRow(index);
for (int i = 0; i < fields.length; i++) {
// 默認(rèn)的行數(shù)從0開(kāi)始,為了統(tǒng)一格式設(shè)置從1開(kāi)始,就是從excel的第二行開(kāi)始
Cell cell = row.createCell(i);
// 為當(dāng)前列賦值
Field field = fields[i];
if (!field.isAccessible()) {
field.setAccessible(true);
}
Object value = (new PropertyDescriptor(field.getName(), clazz)).getReadMethod().invoke(t);
setValue(cell , value);
//設(shè)置數(shù)據(jù)的樣式
cell.setCellStyle(dataStyle);
}
index++;
}
} catch (Exception e) {
e.printStackTrace();
}
return wb;
}
/**
* 設(shè)置cellValue
* 這里可以根據(jù)value的類(lèi)型,進(jìn)行格式化,比如日期格式化
*/
private static void setValue(Cell cell , Object value){
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if(value != null && !"".equals(value)){
if(value instanceof Date){
cell.setCellValue(dateFormat.format(value));
}else{
cell.setCellValue(value.toString());
}
}
}
}
這個(gè)writeToExcelByList,是利用反射,獲取class的fields,然后循環(huán)遍歷插入到cell,這里可以自定義Annotation,然后進(jìn)行一些自定義的操作,比如日期格式化、該field是否導(dǎo)出到excel中等,這里只做簡(jiǎn)單的演示
- 導(dǎo)出結(jié)果
這里的單元格寬度應(yīng)該是可以設(shè)置的,但我就不研究了,有需要再說(shuō)
本文是采用Apache的開(kāi)源項(xiàng)目poi來(lái)實(shí)現(xiàn)的,這個(gè)在企業(yè)中應(yīng)用較多(個(gè)人觀點(diǎn)),還有一個(gè)EasyExcel項(xiàng)目,是alibaba出的,看起來(lái)也不錯(cuò),但我就不嘗試了,放個(gè)鏈接 簡(jiǎn)潔、快速、節(jié)約內(nèi)存的Excel處理工具EasyExcel ,或者直接去官網(wǎng)看文檔吧,更清楚 EasyExcel官方文檔 - 基于Java的Excel處理工具 | Easy Excel
本文參考:Spring boot實(shí)現(xiàn)Excel導(dǎo)出文件
大數(shù)據(jù)量的處理
首先介紹一下目前導(dǎo)出excel的幾種格式:Excel 2003、Excel 2007
Excel 2003:在POI中使用HSSF對(duì)象時(shí),excel 2003最多只允許存儲(chǔ)65536條數(shù)據(jù),一般用來(lái)處理較少的數(shù)據(jù)量。這時(shí)對(duì)于百萬(wàn)級(jí)別數(shù)據(jù),Excel肯定容納不了。
Excel 2007:當(dāng)POI升級(jí)到XSSF對(duì)象時(shí),它可以直接支持excel2007以上版本,因?yàn)樗捎胦oxml格式。這時(shí)excel可以支持1048576條數(shù)據(jù),單個(gè)sheet表就支持近104萬(wàn)條數(shù)據(jù)了,雖然這時(shí)導(dǎo)出100萬(wàn)數(shù)據(jù)能滿足要求,但使用XSSF測(cè)試后發(fā)現(xiàn)偶爾還是會(huì)發(fā)生堆溢出,所以也不適合百萬(wàn)數(shù)據(jù)的導(dǎo)出。
在POI3.8之后新增加了一個(gè)類(lèi),SXSSFWorkbook,采用當(dāng)數(shù)據(jù)加工時(shí)不是類(lèi)似前面版本的對(duì)象,
它可以控制excel數(shù)據(jù)占用的內(nèi)存,他通過(guò)控制在內(nèi)存中的行數(shù)來(lái)實(shí)現(xiàn)資源管理,即當(dāng)創(chuàng)建對(duì)象超過(guò)了設(shè)定的行數(shù),
它會(huì)自動(dòng)刷新內(nèi)存,將數(shù)據(jù)寫(xiě)入文件,這樣導(dǎo)致打印時(shí),占用的CPU,和內(nèi)存很少。
所以可以使用SXXFWorkBook來(lái)實(shí)現(xiàn)百萬(wàn)級(jí)別數(shù)據(jù)量的導(dǎo)出。
只要將上文中的Workbook,替換成SXSSFWorkbook即可
這里參考: Java 使用POI 導(dǎo)出 百萬(wàn)級(jí)別的數(shù)據(jù)量的 Excel
大數(shù)據(jù)量的處理二
其實(shí)上文的大數(shù)據(jù)量處理方式有個(gè)問(wèn)題,比如我要從數(shù)據(jù)庫(kù)查詢(xún)100萬(wàn)的數(shù)據(jù),然后導(dǎo)出到excel,這個(gè)查詢(xún)的過(guò)程(包括數(shù)據(jù)處理的過(guò)程)是很漫長(zhǎng)的,等處理完,頁(yè)面才會(huì)彈出下載彈窗,可能要等待十幾秒甚至幾十秒,不知情的用戶還以為是系統(tǒng)異常了,也就是說(shuō)不能快速響應(yīng),至少我還沒(méi)找到快速響應(yīng)的方式
那么還有另一種方式解決這個(gè)問(wèn)題,就是使用csv文件替代表格(csv可以和表格互相轉(zhuǎn)換且使用效果基本相同)。csv格式文件和txt文件一樣理論上沒(méi)有大小上限,這種方式就不需要poi的依賴(lài)了。
直接上代碼:
entity加上toString方法:
@Override
public String toString() {
return id +
"," + title +
"," + content +
"," + sourceUrl +
"," + imgUrl +
"," + createTime +
"," + updateTime +
"," + delFlag;
}
@RestController
public class ResultController {
@Autowired
private BaiDuResultDao dao;
@GetMapping("/export")
public void exportResult(HttpServletResponse response){
try {
response.reset();
response.setContentType("application/csv;charset=GBK");
response.setHeader("Content-Disposition","attachment;filename=file" + System.currentTimeMillis() + ".csv");
response.setCharacterEncoding("GBK");
PrintWriter out = response.getWriter();
out.println("id,標(biāo)題,內(nèi)容,內(nèi)容來(lái)源,封面圖片,創(chuàng)建時(shí)間,更新時(shí)間,是否刪除");
int limit = 100000;
int pageSize = 10000;
for (int i = 0; i < limit; i++) {
List<BaiDuResult> list = dao.findAllPage(i, pageSize);
for (BaiDuResult baiDuResult : list) {
String str = baiDuResult.toString();
out.println(str);
}
i += pageSize;
}
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
這里可以看到,這里可以用分頁(yè)的方式,將每次分頁(yè)結(jié)果直接響應(yīng)給頁(yè)面,也就是用戶點(diǎn)擊下載,立馬彈窗,然后下載的過(guò)程就耗時(shí)較長(zhǎng)了,這樣用戶的交互會(huì)好一些。
但是?。。。。。?/strong> csv文件會(huì)比excel文件大很多很多,完全不在一個(gè)數(shù)量級(jí),因?yàn)閏sv可以用記事本打開(kāi),本質(zhì)上是字符串文件,沒(méi)有任何壓縮,xls之類(lèi)的都是有壓縮的,需要專(zhuān)門(mén)軟件打開(kāi)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-477903.html
參考:java實(shí)現(xiàn)大數(shù)據(jù)Excel導(dǎo)出文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-477903.html
到了這里,關(guān)于springboot項(xiàng)目實(shí)現(xiàn)excel導(dǎo)出的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!