@ -11,13 +11,12 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service ;
import java.io.IOException ;
import java.net.MalformedURLException ;
import java.net.URL ;
import java.net.URLEncoder ;
import java.nio.charset.StandardCharsets ;
import java.util.List ;
import java.util.Map ;
import java.util.concurrent.Semaphore ;
import java.util.concurrent.* ;
import java.util.stream.Collectors ;
/**
@ -29,6 +28,9 @@ import java.util.stream.Collectors;
public class DeviceExportService {
public void export ( List < Device > devices , HttpServletResponse response ) {
long startTime = System . currentTimeMillis ( ) ;
log . info ( " 开始导出设备列表,设备数量: {} " , devices . size ( ) ) ;
try {
String fileName = " 设备列表_ " + System . currentTimeMillis ( ) + " .xlsx " ;
// 使用URLEncoder进行RFC 5987编码
@ -39,8 +41,9 @@ public class DeviceExportService {
// 使用RFC 5987标准编码文件名
response . setHeader ( " Content-disposition " , " attachment;filename*=UTF-8'' " + encodedFileName ) ;
// 转换为DTO列表
List < DeviceExcelExportDTO > dtoList = devices . s tream( ) . map ( device - > {
// 转换为DTO列表,使用并行流加速处理
List < DeviceExcelExportDTO > dtoList = devices . parallelS tream( ) . map ( device - > {
long deviceProcessStartTime = System . currentTimeMillis ( ) ;
DeviceExcelExportDTO dto = new DeviceExcelExportDTO ( ) ;
dto . setDeviceName ( device . getDeviceName ( ) ) ;
dto . setDeviceMac ( device . getDeviceMac ( ) ) ;
@ -59,13 +62,22 @@ public class DeviceExportService {
// 处理图片URL转换
handleDevicePic ( device , dto ) ;
long deviceProcessEndTime = System . currentTimeMillis ( ) ;
log . info ( " 单个设备处理耗时: {} ms, 设备ID: {} " , ( deviceProcessEndTime - deviceProcessStartTime ) , device . getId ( ) ) ;
return dto ;
} ) . collect ( Collectors . toList ( ) ) ;
// 写入Excel
long excelWriteStartTime = System . currentTimeMillis ( ) ;
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 ) {
log . error ( " 导出Excel失败 " , e ) ;
throw new RuntimeException ( " 导出Excel失败 " , e ) ;
}
}
@ -79,6 +91,9 @@ public class DeviceExportService {
* @param response
*/
public void exportWithTypeInfo ( List < Device > devices , List < DeviceType > deviceTypes , HttpServletResponse response ) {
long startTime = System . currentTimeMillis ( ) ;
log . info ( " 开始导出设备列表(含类型详情),设备数量: {} " , devices . size ( ) ) ;
try {
String fileName = " 设备列表_含类型详情_ " + System . currentTimeMillis ( ) + " .xlsx " ;
// 使用URLEncoder进行RFC 5987编码
@ -93,8 +108,9 @@ public class DeviceExportService {
Map < Long , DeviceType > deviceTypeMap = deviceTypes . stream ( )
. collect ( Collectors . toMap ( DeviceType : : getId , deviceType - > deviceType ) ) ;
// 转换为DTO列表
List < DeviceWithTypeExcelExportDTO > dtoList = devices . s tream( ) . map ( device - > {
// 转换为DTO列表,使用并行流加速处理
List < DeviceWithTypeExcelExportDTO > dtoList = devices . parallelS tream( ) . map ( device - > {
long deviceProcessStartTime = System . currentTimeMillis ( ) ;
DeviceWithTypeExcelExportDTO dto = new DeviceWithTypeExcelExportDTO ( ) ;
dto . setDeviceName ( device . getDeviceName ( ) ) ;
dto . setDeviceMac ( device . getDeviceMac ( ) ) ;
@ -149,14 +165,20 @@ public class DeviceExportService {
// 处理图片URL转换
handleDevicePicForTypeExport ( device , dto ) ;
return dto ;
} ) . collect ( Collectors . toList ( ) ) ;
// 写入Excel
long excelWriteStartTime = System . currentTimeMillis ( ) ;
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 ) {
log . error ( " 导出Excel失败 " , e ) ;
throw new RuntimeException ( " 导出Excel失败 " , e ) ;
}
}
@ -224,71 +246,67 @@ public class 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 ) {
String picUrl = device . getDevicePic ( ) ;
log . debug ( " 处理设备图片, 设备ID: {}, 图片URL: {} " , device . getId ( ) , picUrl ) ;
if ( picUrl ! = null & & ! picUrl . trim ( ) . isEmpty ( ) ) {
try {
// 获取加载图片的许可
imageLoadSemaphore . a cquire( ) ;
// 获取加载图片的许可,带超时控制
if ( ! imageLoadSemaphore . tryA cquire( 5 , TimeUnit . SECONDS ) ) {
dto . setDevicePic ( null ) ;
return ;
}
try {
// 自动将HTTP转换为HTTPS以避免重定向问题
if ( picUrl . startsWith ( " http:// " ) ) {
picUrl = " https:// " + picUrl . substring ( 7 ) ;
log . debug ( " 自动将HTTP转换为HTTPS: {} " , picUrl ) ;
}
// 尝试创建URL对象( 会自动验证格式)
URL url = new URL ( picUrl ) ;
dto . setDevicePic ( url ) ;
log . debug ( " 成功设置设备图片URL到DTO " ) ;
} finally {
// 释放许可
imageLoadSemaphore . release ( ) ;
}
} catch ( Exception e ) {
log . warn ( " 设置设备图片失败, 设备ID: {}, URL: {}, 错误: {} " , device . getId ( ) , picUrl , e . getMessage ( ) ) ;
dto . setDevicePic ( null ) ;
}
} else {
log . debug ( " 设备没有设置图片, 设备ID: {} " , device . getId ( ) ) ;
dto . setDevicePic ( null ) ;
}
}
private void handleDevicePicForTypeExport ( Device device , DeviceWithTypeExcelExportDTO dto ) {
String picUrl = device . getDevicePic ( ) ;
log . debug ( " 处理设备图片, 设备ID: {}, 图片URL: {} " , device . getId ( ) , picUrl ) ;
if ( picUrl ! = null & & ! picUrl . trim ( ) . isEmpty ( ) ) {
try {
// 获取加载图片的许可
imageLoadSemaphore . a cquire( ) ;
// 获取加载图片的许可,带超时控制
if ( ! imageLoadSemaphore . tryA cquire( 5 , TimeUnit . SECONDS ) ) {
dto . setDevicePic ( null ) ;
return ;
}
try {
picUrl = convertUrl ( picUrl ) ;
log . info ( " 转换后的URL: {} " , picUrl ) ;
// 尝试创建URL对象( 会自动验证格式)
URL url = new URL ( picUrl ) ;
dto . setDevicePic ( url ) ;
log . debug ( " 成功设置设备图片URL到DTO " ) ;
} finally {
// 释放许可
imageLoadSemaphore . release ( ) ;
}
} catch ( Exception e ) {
log . warn ( " 设置设备图片失败, 设备ID: {}, URL: {}, 错误: {} " , device . getId ( ) , picUrl , e . getMessage ( ) ) ;
dto . setDevicePic ( null ) ;
}
} else {
log . debug ( " 设备没有设置图片, 设备ID: {} " , device . getId ( ) ) ;
dto . setDevicePic ( null ) ;
}
}