优化设备导出
This commit is contained in:
@ -69,9 +69,9 @@ spring:
|
|||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
# 单个文件大小
|
# 单个文件大小
|
||||||
max-file-size: 10MB
|
max-file-size: 100MB
|
||||||
# 设置总上传的文件大小
|
# 设置总上传的文件大小
|
||||||
max-request-size: 20MB
|
max-request-size: 200MB
|
||||||
mvc:
|
mvc:
|
||||||
# 设置静态资源路径 防止所有请求都去查静态资源
|
# 设置静态资源路径 防止所有请求都去查静态资源
|
||||||
static-path-pattern: /static/**
|
static-path-pattern: /static/**
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
|
|||||||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||||
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
|
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
|
||||||
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
|
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
|
||||||
|
import com.fuyuanshen.equipment.converter.IgnoreFailedImageConverter;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@ -27,7 +28,8 @@ public class DeviceWithTypeExcelExportDTO {
|
|||||||
@ColumnWidth(20)
|
@ColumnWidth(20)
|
||||||
private String typeName;
|
private String typeName;
|
||||||
|
|
||||||
@ExcelProperty(value = "设备图片")
|
@ExcelProperty(value = "设备图片", converter = IgnoreFailedImageConverter.class)
|
||||||
|
// @ExcelProperty(value = "设备图片")
|
||||||
@ColumnWidth(30) // 设置图片列宽度
|
@ColumnWidth(30) // 设置图片列宽度
|
||||||
private URL devicePic; // 使用URL类型
|
private URL devicePic; // 使用URL类型
|
||||||
|
|
||||||
|
|||||||
@ -11,13 +11,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,6 +28,9 @@ import java.util.stream.Collectors;
|
|||||||
public class DeviceExportService {
|
public class DeviceExportService {
|
||||||
|
|
||||||
public void export(List<Device> devices, HttpServletResponse response) {
|
public void export(List<Device> devices, HttpServletResponse response) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
log.info("开始导出设备列表,设备数量: {}", devices.size());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String fileName = "设备列表_" + System.currentTimeMillis() + ".xlsx";
|
String fileName = "设备列表_" + System.currentTimeMillis() + ".xlsx";
|
||||||
// 使用URLEncoder进行RFC 5987编码
|
// 使用URLEncoder进行RFC 5987编码
|
||||||
@ -39,8 +41,9 @@ public class DeviceExportService {
|
|||||||
// 使用RFC 5987标准编码文件名
|
// 使用RFC 5987标准编码文件名
|
||||||
response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + encodedFileName);
|
response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + encodedFileName);
|
||||||
|
|
||||||
// 转换为DTO列表
|
// 转换为DTO列表,使用并行流加速处理
|
||||||
List<DeviceExcelExportDTO> dtoList = devices.stream().map(device -> {
|
List<DeviceExcelExportDTO> dtoList = devices.parallelStream().map(device -> {
|
||||||
|
long deviceProcessStartTime = System.currentTimeMillis();
|
||||||
DeviceExcelExportDTO dto = new DeviceExcelExportDTO();
|
DeviceExcelExportDTO dto = new DeviceExcelExportDTO();
|
||||||
dto.setDeviceName(device.getDeviceName());
|
dto.setDeviceName(device.getDeviceName());
|
||||||
dto.setDeviceMac(device.getDeviceMac());
|
dto.setDeviceMac(device.getDeviceMac());
|
||||||
@ -59,13 +62,22 @@ public class DeviceExportService {
|
|||||||
// 处理图片URL转换
|
// 处理图片URL转换
|
||||||
handleDevicePic(device, dto);
|
handleDevicePic(device, dto);
|
||||||
|
|
||||||
|
long deviceProcessEndTime = System.currentTimeMillis();
|
||||||
|
log.info("单个设备处理耗时: {} ms, 设备ID: {}", (deviceProcessEndTime - deviceProcessStartTime), device.getId());
|
||||||
return dto;
|
return dto;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
// 写入Excel
|
// 写入Excel
|
||||||
|
long excelWriteStartTime = System.currentTimeMillis();
|
||||||
EasyExcel.write(response.getOutputStream(), DeviceExcelExportDTO.class).sheet("设备数据").doWrite(dtoList);
|
EasyExcel.write(response.getOutputStream(), DeviceExcelExportDTO.class).sheet("设备数据").doWrite(dtoList);
|
||||||
|
long excelWriteEndTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
log.info("设备列表导出完成,总耗时: {} ms,设备数量: {},Excel写入耗时: {} ms",
|
||||||
|
(endTime - startTime), devices.size(), (excelWriteEndTime - excelWriteStartTime));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
log.error("导出Excel失败", e);
|
||||||
throw new RuntimeException("导出Excel失败", e);
|
throw new RuntimeException("导出Excel失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,6 +91,9 @@ public class DeviceExportService {
|
|||||||
* @param response
|
* @param response
|
||||||
*/
|
*/
|
||||||
public void exportWithTypeInfo(List<Device> devices, List<DeviceType> deviceTypes, HttpServletResponse response) {
|
public void exportWithTypeInfo(List<Device> devices, List<DeviceType> deviceTypes, HttpServletResponse response) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
log.info("开始导出设备列表(含类型详情),设备数量: {}", devices.size());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String fileName = "设备列表_含类型详情_" + System.currentTimeMillis() + ".xlsx";
|
String fileName = "设备列表_含类型详情_" + System.currentTimeMillis() + ".xlsx";
|
||||||
// 使用URLEncoder进行RFC 5987编码
|
// 使用URLEncoder进行RFC 5987编码
|
||||||
@ -93,8 +108,9 @@ public class DeviceExportService {
|
|||||||
Map<Long, DeviceType> deviceTypeMap = deviceTypes.stream()
|
Map<Long, DeviceType> deviceTypeMap = deviceTypes.stream()
|
||||||
.collect(Collectors.toMap(DeviceType::getId, deviceType -> deviceType));
|
.collect(Collectors.toMap(DeviceType::getId, deviceType -> deviceType));
|
||||||
|
|
||||||
// 转换为DTO列表
|
// 转换为DTO列表,使用并行流加速处理
|
||||||
List<DeviceWithTypeExcelExportDTO> dtoList = devices.stream().map(device -> {
|
List<DeviceWithTypeExcelExportDTO> dtoList = devices.parallelStream().map(device -> {
|
||||||
|
long deviceProcessStartTime = System.currentTimeMillis();
|
||||||
DeviceWithTypeExcelExportDTO dto = new DeviceWithTypeExcelExportDTO();
|
DeviceWithTypeExcelExportDTO dto = new DeviceWithTypeExcelExportDTO();
|
||||||
dto.setDeviceName(device.getDeviceName());
|
dto.setDeviceName(device.getDeviceName());
|
||||||
dto.setDeviceMac(device.getDeviceMac());
|
dto.setDeviceMac(device.getDeviceMac());
|
||||||
@ -149,14 +165,20 @@ public class DeviceExportService {
|
|||||||
|
|
||||||
// 处理图片URL转换
|
// 处理图片URL转换
|
||||||
handleDevicePicForTypeExport(device, dto);
|
handleDevicePicForTypeExport(device, dto);
|
||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
// 写入Excel
|
// 写入Excel
|
||||||
|
long excelWriteStartTime = System.currentTimeMillis();
|
||||||
EasyExcel.write(response.getOutputStream(), DeviceWithTypeExcelExportDTO.class).sheet("设备数据含类型详情").doWrite(dtoList);
|
EasyExcel.write(response.getOutputStream(), DeviceWithTypeExcelExportDTO.class).sheet("设备数据含类型详情").doWrite(dtoList);
|
||||||
|
long excelWriteEndTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
log.info("设备列表(含类型详情)导出完成,总耗时: {} ms,设备数量: {},Excel写入耗时: {} ms",
|
||||||
|
(endTime - startTime), devices.size(), (excelWriteEndTime - excelWriteStartTime));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
log.error("导出Excel失败", e);
|
||||||
throw new RuntimeException("导出Excel失败", e);
|
throw new RuntimeException("导出Excel失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,71 +246,67 @@ public class DeviceExportService {
|
|||||||
|
|
||||||
|
|
||||||
// 在DeviceExportService中添加并发控制
|
// 在DeviceExportService中添加并发控制
|
||||||
private static final Semaphore imageLoadSemaphore = new Semaphore(5); // 最多同时加载5张图片
|
private static final Semaphore imageLoadSemaphore = new Semaphore(10); // 增加到最多同时加载10张图片
|
||||||
|
|
||||||
private void handleDevicePic(Device device, DeviceExcelExportDTO dto) {
|
private void handleDevicePic(Device device, DeviceExcelExportDTO dto) {
|
||||||
String picUrl = device.getDevicePic();
|
String picUrl = device.getDevicePic();
|
||||||
log.debug("处理设备图片,设备ID: {}, 图片URL: {}", device.getId(), picUrl);
|
|
||||||
|
|
||||||
if (picUrl != null && !picUrl.trim().isEmpty()) {
|
if (picUrl != null && !picUrl.trim().isEmpty()) {
|
||||||
try {
|
try {
|
||||||
// 获取加载图片的许可
|
// 获取加载图片的许可,带超时控制
|
||||||
imageLoadSemaphore.acquire();
|
if (!imageLoadSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
|
||||||
|
dto.setDevicePic(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 自动将HTTP转换为HTTPS以避免重定向问题
|
// 自动将HTTP转换为HTTPS以避免重定向问题
|
||||||
if (picUrl.startsWith("http://")) {
|
if (picUrl.startsWith("http://")) {
|
||||||
picUrl = "https://" + picUrl.substring(7);
|
picUrl = "https://" + picUrl.substring(7);
|
||||||
log.debug("自动将HTTP转换为HTTPS: {}", picUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试创建URL对象(会自动验证格式)
|
// 尝试创建URL对象(会自动验证格式)
|
||||||
URL url = new URL(picUrl);
|
URL url = new URL(picUrl);
|
||||||
dto.setDevicePic(url);
|
dto.setDevicePic(url);
|
||||||
log.debug("成功设置设备图片URL到DTO");
|
|
||||||
} finally {
|
} finally {
|
||||||
// 释放许可
|
// 释放许可
|
||||||
imageLoadSemaphore.release();
|
imageLoadSemaphore.release();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("设置设备图片失败,设备ID: {}, URL: {}, 错误: {}", device.getId(), picUrl, e.getMessage());
|
|
||||||
dto.setDevicePic(null);
|
dto.setDevicePic(null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug("设备没有设置图片,设备ID: {}", device.getId());
|
|
||||||
dto.setDevicePic(null);
|
dto.setDevicePic(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void handleDevicePicForTypeExport(Device device, DeviceWithTypeExcelExportDTO dto) {
|
private void handleDevicePicForTypeExport(Device device, DeviceWithTypeExcelExportDTO dto) {
|
||||||
String picUrl = device.getDevicePic();
|
String picUrl = device.getDevicePic();
|
||||||
log.debug("处理设备图片,设备ID: {}, 图片URL: {}", device.getId(), picUrl);
|
|
||||||
|
|
||||||
if (picUrl != null && !picUrl.trim().isEmpty()) {
|
if (picUrl != null && !picUrl.trim().isEmpty()) {
|
||||||
try {
|
try {
|
||||||
// 获取加载图片的许可
|
// 获取加载图片的许可,带超时控制
|
||||||
imageLoadSemaphore.acquire();
|
if (!imageLoadSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
|
||||||
|
dto.setDevicePic(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
picUrl = convertUrl(picUrl);
|
picUrl = convertUrl(picUrl);
|
||||||
log.info("转换后的URL: {}", picUrl);
|
|
||||||
|
|
||||||
// 尝试创建URL对象(会自动验证格式)
|
// 尝试创建URL对象(会自动验证格式)
|
||||||
URL url = new URL(picUrl);
|
URL url = new URL(picUrl);
|
||||||
dto.setDevicePic(url);
|
dto.setDevicePic(url);
|
||||||
log.debug("成功设置设备图片URL到DTO");
|
|
||||||
} finally {
|
} finally {
|
||||||
// 释放许可
|
// 释放许可
|
||||||
imageLoadSemaphore.release();
|
imageLoadSemaphore.release();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("设置设备图片失败,设备ID: {}, URL: {}, 错误: {}", device.getId(), picUrl, e.getMessage());
|
|
||||||
dto.setDevicePic(null);
|
dto.setDevicePic(null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug("设备没有设置图片,设备ID: {}", device.getId());
|
|
||||||
dto.setDevicePic(null);
|
dto.setDevicePic(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user