-
Notifications
You must be signed in to change notification settings - Fork 197
10.使用Easy Excel导出聊天记录
JustCoding-Hai edited this page Nov 8, 2020
·
1 revision
导入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
EasyExcel提供的五种图片导出的方式:https://www.yuque.com/easyexcel/doc/write#cb1b271f
因为我想把聊天记录中的图片内容也导出到Excel中,而图片的保存的是URL地址。另外因为GroupMsgContent的content属性可能存储普通的文本和图片URL。
定义一个URL转换器的做法,新键一个类GroupMsgContentData来分开处理普通的文本和图片URL。
/**
* Excel导入导出的数据类
* @author Hai
* @date 2020/10/7 - 23:52
*/
@ColumnWidth(25)
@ContentRowHeight(30)
public class GroupMsgContentData {
@ExcelProperty("消息内容编号")
private Integer id;
@ExcelProperty("发送者的编号")
private Integer fromId;
@ExcelProperty("昵称")
private String fromName;
@ExcelIgnore
private URL fromProfile;
@ExcelProperty("发送时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createTime;
@ExcelProperty(value = {"内容","文本"})
@ColumnWidth(50)
private String textContent;
@ExcelProperty(value = {"内容","图片"},converter = MyUrlImageConverter.class)
@ColumnWidth(50)
private URL imageContent;
@ExcelIgnore
private Integer messageTypeId;
}
对应的MyUrlImageConverter
/**
* @author Hai
* @program: subtlechat
* @description: 将URL图片的格式转化器
* @create 2020/10/8 - 12:45
**/
public class MyUrlImageConverter implements Converter<URL> {
@Override
public Class supportJavaTypeKey() {
return URL.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.IMAGE;
}
@Override
public URL convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
throw new UnsupportedOperationException("Cannot convert images to url.");
}
@Override
public CellData convertToExcelData(URL value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws IOException {
InputStream inputStream = null;
try {
//开启连接
URLConnection uc = value.openConnection();
URL url = null;
//获取响应状态
int statusCode = ((HttpURLConnection) uc).getResponseCode();
switch (statusCode){
case 200:
inputStream = value.openStream();
break;
case 404:
//默认给一个图片
url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598096095144&di=9a72ad26e83effb9341c711c9818b85f&imgtype=0&src=http%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F11%2F69%2Fj2AjnHspwT.jpg");
inputStream = url.openStream();
break;
default :
url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598096095144&di=9a72ad26e83effb9341c711c9818b85f&imgtype=0&src=http%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F11%2F69%2Fj2AjnHspwT.jpg");
inputStream = url.openStream();
break;
}
byte[] bytes = IoUtils.toByteArray(inputStream);
byte[] compressBytes = ImgUtil.compressPicForScale(bytes,200, UUID.randomUUID().toString());
return new CellData(compressBytes);
}catch (ConnectException exception){
//捕获下链接异常
URL url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598096095144&di=9a72ad26e83effb9341c711c9818b85f&imgtype=0&src=http%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F11%2F69%2Fj2AjnHspwT.jpg");
inputStream = url.openStream();
byte[] bytes = IoUtils.toByteArray(inputStream);
return new CellData(bytes);
}catch (FileNotFoundException fileNotFoundException){
URL url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598096095144&di=9a72ad26e83effb9341c711c9818b85f&imgtype=0&src=http%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F11%2F69%2Fj2AjnHspwT.jpg");
inputStream = url.openStream();
byte[] bytes = IoUtils.toByteArray(inputStream);
return new CellData(bytes);
}finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}
在类GroupMsgContent中添加转换为GroupMsgContentData的方法
/**
* 将数据库实体转化为Excel的数据实体
* @param groupMsgContent
* @return
*/
public static GroupMsgContentData convertEntityToData(GroupMsgContent groupMsgContent) throws MalformedURLException {
GroupMsgContentData groupMsgContentData = new GroupMsgContentData();
groupMsgContentData.setFromId(groupMsgContent.getFromId());
groupMsgContentData.setId(groupMsgContent.getId());
groupMsgContentData.setFromName(groupMsgContent.getFromName());
groupMsgContentData.setCreateTime(groupMsgContent.getCreateTime());
//转化为URL以Excel导出图片
groupMsgContentData.setFromProfile(new URL(groupMsgContent.getFromProfile()));
//根据消息类型设置内容
if (groupMsgContent.getMessageTypeId()==1){
groupMsgContentData.setTextContent(groupMsgContent.getContent());
}
if (groupMsgContent.getMessageTypeId()==2){
groupMsgContentData.setImageContent(new URL(groupMsgContent.getContent()));
}
return groupMsgContentData;
}
在Excel操作前,获取所有的GroupMsgContent,然后通过Stream处理获取转换后的GroupMsgContentData
RespPageBean allGroupMsgContentByPage = getAllGroupMsgContentByPage(null, null, null, null, null);
List<GroupMsgContent> data = (List<GroupMsgContent>) allGroupMsgContentByPage.getData();
//转化数据为用于Excel导出的格式
List<GroupMsgContentData> convertedData = data.stream().map(item -> {
try {
return GroupMsgContent.convertEntityToData(item);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return new GroupMsgContentData();
}).collect(Collectors.toList());
这样将GroupMsgContentData作为导出数据的映射类。但该做法增加了一次遍历,当数据量大时,浪费的时间更加明显。
定义一个Converter同时处理图片和文本。如果图片处理出错,就当成是字符串处理。
/**
* @author Hai
* @program: subtlechat
* @description: 处理聊天记录的内容的导出
* @create 2020/10/8 - 23:59
**/
public class MyContentConverter implements Converter<String> {
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.IMAGE;
}
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
throw new UnsupportedOperationException("Cannot convert images to string");
}
@Override
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws IOException {
if (value.startsWith("http")){
InputStream inputStream = null;
URL imageUrl = new URL(value);
try {
//开启连接
URLConnection uc = imageUrl.openConnection();
URL url = null;
//获取响应状态
int statusCode = ((HttpURLConnection) uc).getResponseCode();
switch (statusCode){
case 200:
inputStream = imageUrl.openStream();
break;
default :
//直接当成String处理
return new CellData(value);
}
byte[] bytes = IoUtils.toByteArray(inputStream);
//压缩图片
byte[] compressBytes = ImgUtil.compressPicForScale(bytes,200, UUID.randomUUID().toString());
return new CellData(compressBytes);
}catch (Exception exception){
return new CellData(value);
}finally {
if (inputStream != null) {
inputStream.close();
}
}
}
else{
return new CellData(value);
}
}
}
直接在GroupMsgContent中指定转化器
/**
* (GroupMsgContent)实体类
*
* @author makejava
* @since 2020-06-17 10:46:50
*/
@ColumnWidth(25)
@ContentRowHeight(40)
public class GroupMsgContent implements Serializable {
private static final long serialVersionUID = 980328865610261046L;
/**
* 消息内容编号
*/
@ExcelProperty("消息内容编号")
private Integer id;
/**
* 发送者的编号
*/
@ExcelProperty("发送者的编号")
private Integer fromId;
/**
* 发送者的昵称
*/
@ExcelProperty("昵称")
private String fromName;
/**
* 发送者的头像
*/
@ExcelIgnore
private String fromProfile;
/**
* 消息发送时间
*/
@ExcelProperty("发送时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createTime;
/**
* 消息内容
*/
@ExcelProperty(value = "内容",converter = MyContentConverter.class)
@ColumnWidth(50)
private String content;
/**
* 消息类型编号
*/
@ExcelIgnore
private Integer messageTypeId;
}
上面使用到的图片压缩使用的是Thumbnails插件
/**
* @author Hai
* @program: subtlechat
* @description: 调用Thumbnails压缩图片
* @create 2020/10/8 - 13:50
**/
public class ImgUtil {
private static Logger logger = LoggerFactory.getLogger(ImgUtil.class);
/**
* 根据指定大小压缩图片
*
* @param imageBytes 源图片字节数组
* @param desFileSize 指定图片大小,单位kb
* @param imageId 影像编号
* @return 压缩质量后的图片字节数组
*/
public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize, String imageId) {
if (imageBytes == null || imageBytes.length <= 0 || imageBytes.length < desFileSize * 1024) {
return imageBytes;
}
long srcSize = imageBytes.length;
double accuracy=0.4;
try {
while (imageBytes.length > desFileSize * 1024) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
Thumbnails.of(inputStream)
.scale(accuracy)
.outputQuality(accuracy)
.toOutputStream(outputStream);
imageBytes = outputStream.toByteArray();
}
logger.info("【图片压缩】imageId={} | 图片原大小={}kb | 压缩后大小={}kb",
imageId, srcSize / 1024, imageBytes.length / 1024);
} catch (Exception e) {
logger.error("【图片压缩】msg=图片压缩失败!", e);
}
return imageBytes;
}
}
依照官方文档处理:https://www.yuque.com/easyexcel/doc/write#afb7324a
将文件写入到
/**
* 处理群聊记录的导出,提供
* @param response
* @throws IOException
*/
@Override
public void handleDownload(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
//设置文件信息 这里URLEncoder.encode可以防止中文乱码
String fileName = URLEncoder.encode("群聊记录", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
RespPageBean allGroupMsgContentByPage = getAllGroupMsgContentByPage(null, null, null, null, null);
List<GroupMsgContent> data = (List<GroupMsgContent>) allGroupMsgContentByPage.getData();
//转化数据为用于Excel导出的格式
List<GroupMsgContentData> convertedData = data.stream().map(item -> {
try {
return GroupMsgContent.convertEntityToData(item);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return new GroupMsgContentData();
}).collect(Collectors.toList());
//写出数据到HttpServletResponse中
EasyExcel.write(response.getOutputStream(),GroupMsgContentData.class).sheet("sheet1").doWrite(convertedData);
}
使用JS的内置方法window.open方法处理。
//导出数据到Excel文件中
exportData(){
window.open("/groupMsgContent/download","_parent");
},
问题:因为导出图片到Excel中导致写Excel的速度太慢了。
解决:
1.前端显示优化,增加下载的进度条
2.选择其他POI技术
3.增加JVM内存
-
多线程处理
-
导出不处理图片了,只显示url