当前位置: 首页 > wzjs >正文

加快建设公司新版网站网络推广团队

加快建设公司新版网站,网络推广团队,网络安全等级保护,网站后期维护需要注意什么背景:在旧系统的基础上,导出一些工单信息时,现需要新添加处理人的签名或者签章,这就涉及图片的上传、下载、写入等几个操作。 1、EasyExcel工具类 (1)支持下拉框的导出。 import com.alibaba.excel.Easy…

背景:在旧系统的基础上,导出一些工单信息时,现需要新添加处理人的签名或者签章,这就涉及图片的上传、下载、写入等几个操作。

1、EasyExcel工具类

(1)支持下拉框的导出。

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com..common.handler.DropDownHandler;
import com..modules.device.convert.ImageCellWriteHandler;
import com..modules.device.domain.vo.DeviceAndTypeVO;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;@Slf4j
public class ExcelExportUtil {private static final String CHARACTER = "UTF-8";private static final String CONTENT_TYPE = "application/vnd.ms-excel;charset=utf-8";private static final String CONTENT_DISPOSITION = "Content-Disposition";private static final String CACHE_CONTROL = "Cache-Control";private static final String NO_STORE = "no-store";private static final String MAX_AGE = "max-age=0";private static final String PASSWORD = "excel_unlock";private static final Integer colSplit = 0;private static final Integer rowSplit = 1;private static final Integer leftmostColumn = 0;private static final Integer topRow = 1; 
/*** 通用导出 Excel 方法(支持下拉框和每行图片插入)** @param response         HttpServletResponse* @param data             导出数据,数据行顺序与 Excel 中顺序一致(表头在第0行,第一条数据为第1行)* @param clazz            数据模型 Class* @param fileName         导出文件名(不包含后缀)* @param dropDownHandler  下拉框处理器(可选,可传 null)* @param imagesList       每条记录对应的图片数据列表,List中每个元素为 byte[2] 数组,*                         第0位为签名图片, 第1位为签章图片(可有可无)* @param signCol          签名图片插入列(0-based)* @param signatureCol     签章图片插入列(0-based)* @throws IOException     I/O 异常时抛出*/public static void exportExcel(HttpServletResponse response, List<?> data, Class<?> clazz, String fileName,Object dropDownHandler, List<byte[][]> imagesList,int signCol, int signatureCol) throws IOException {String fullFileName = fileName + ".xlsx";String encodedFileName = URLEncoder.encode(fullFileName, StandardCharsets.UTF_8.toString());response.setCharacterEncoding(CHARACTER);response.setContentType(CONTENT_TYPE);response.setHeader(CONTENT_DISPOSITION, "attachment; filename=" + encodedFileName);response.setHeader(CACHE_CONTROL, NO_STORE);response.addHeader(CACHE_CONTROL, MAX_AGE);ServletOutputStream out = null;ExcelWriter excelWriter = null;try {out = response.getOutputStream();ExcelWriterBuilder writerBuilder = EasyExcelFactory.write(out, clazz);// 注册下拉框处理器(需确保 dropDownHandler 是 WriteHandler 类型)if (dropDownHandler != null && dropDownHandler instanceof WriteHandler) {writerBuilder.registerWriteHandler((WriteHandler) dropDownHandler);}excelWriter = writerBuilder.build();WriteSheet writeSheet = EasyExcelFactory.writerSheet(fileName).build();excelWriter.write(data, writeSheet);// 获取 Workbook 和 SheetWorkbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();Sheet sheet = workbook.getSheetAt(0);// 遍历数据行插入图片(从第1行开始)if (imagesList != null && !imagesList.isEmpty()) {for (int i = 0; i < data.size(); i++) {int targetRowIndex = i + 1; // 数据行从第1行开始if (i < imagesList.size()) {byte[][] images = imagesList.get(i);if (images != null) {insertImages(sheet, workbook, targetRowIndex, images, signCol, signatureCol);}}}}// 必须显式调用 finish 确保数据写入流excelWriter.finish();out.flush();} catch (Exception e) {throw new IOException("导出 Excel 失败: " + e.getMessage(), e);} finally {// 手动关闭资源(注意顺序:先关闭 excelWriter,再关闭流)if (excelWriter != null) {excelWriter.finish(); // finish() 包含关闭操作}if (out != null) {try {out.close();} catch (IOException e) {log.error("关闭输出流异常: ", e);}}}}/*** 通用的图片插入方法,在指定的 Excel 行中插入签名和签章图片** @param sheet         目标 Sheet* @param workbook      Excel Workbook* @param targetRow     目标行索引(0-based;表头为0,第一条数据为1)* @param images        byte[2] 数组,其中 images[0] 为签名图片,images[1] 为签章图片* @param signCol       签名图片插入列(0-based)* @param signatureCol  签章图片插入列(0-based)*/private static void insertImages(Sheet sheet, Workbook workbook, int targetRow, byte[][] images,int signCol, int signatureCol) {if (sheet == null || workbook == null) {return;}Row row = sheet.getRow(targetRow);if (row == null) {row = sheet.createRow(targetRow);}Drawing<?> drawing = sheet.createDrawingPatriarch();if (images[0] != null && images[0].length > 0) {int pictureIdx = workbook.addPicture(images[0], Workbook.PICTURE_TYPE_PNG);ClientAnchor anchor = new XSSFClientAnchor(0, 0, 1023, 255, signCol, targetRow, signCol + 1, targetRow + 1);drawing.createPicture(anchor, pictureIdx);}if (images[1] != null && images[1].length > 0) {int pictureIdx = workbook.addPicture(images[1], Workbook.PICTURE_TYPE_PNG);ClientAnchor anchor = new XSSFClientAnchor(0, 0, 1023, 255, signatureCol, targetRow, signatureCol + 1, targetRow + 1);drawing.createPicture(anchor, pictureIdx);}}
}

(2)无下拉框实现

/*** 配合insertImages(Sheet sheet, Workbook workbook, byte[] signImage, byte[] signatureImage,*                                      int targetRowIndex, int signCol, int signatureCol) 方法使用* @param response* @param data* @param clazz* @param fileName* @param signImage* @param signatureImage* @param targetRow* @param signCol* @param signatureCol* @throws IOException*/public static void exportExcel(HttpServletResponse response, List<?> data, Class<?> clazz, String fileName,byte[] signImage, byte[] signatureImage,int targetRow, int signCol, int signatureCol) throws IOException {String encodedFileName = URLEncoder.encode(fileName + ".xlsx", "UTF-8");response.setCharacterEncoding("UTF-8");response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);response.setHeader("Cache-Control", "no-store");response.addHeader("Cache-Control", "max-age=0");ExcelWriter excelWriter = null;ServletOutputStream out = null;try {out = response.getOutputStream();excelWriter = EasyExcelFactory.write(out, clazz).build();WriteSheet writeSheet = EasyExcelFactory.writerSheet(fileName).build();excelWriter.write(data, writeSheet);// 获取 Workbook 和 SheetWorkbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();Sheet sheet = workbook.getSheetAt(0);// 插入图片insertImages(sheet, workbook, signImage, signatureImage, targetRow, signCol, signatureCol);// 必须显式调用 finish 确保写入完成excelWriter.finish();out.flush();} finally {// 手动关闭资源(EasyExcel 3.x 中 finish() 已包含关闭操作)if (excelWriter != null) {excelWriter.finish(); // 确保资源释放}if (out != null) {out.close();}}}/*** 通用inserImages方法,和没有的DropDownHandler dropDownHandler参数的exportExcel方法一起配合使用* @param sheet* @param workbook* @param signImage* @param signatureImage* @param targetRowIndex* @param signCol* @param signatureCol*/private static void insertImages(Sheet sheet, Workbook workbook, byte[] signImage, byte[] signatureImage,int targetRowIndex, int signCol, int signatureCol) {if (sheet == null || workbook == null) {log.warn("插入图片失败: sheet 或 workbook 为空");return;}// 确保目标行存在Row row = sheet.getRow(targetRowIndex);if (row == null) {row = sheet.createRow(targetRowIndex);}Drawing<?> drawing = sheet.createDrawingPatriarch();// 插入签名图片if (signImage != null && signImage.length > 0) {int pictureIdx = workbook.addPicture(signImage, Workbook.PICTURE_TYPE_PNG);ClientAnchor anchor = new XSSFClientAnchor(0, 0, 1023, 255, signCol, targetRowIndex, signCol + 1, targetRowIndex + 1);drawing.createPicture(anchor, pictureIdx);}// 插入签章图片if (signatureImage != null && signatureImage.length > 0) {int pictureIdx = workbook.addPicture(signatureImage, Workbook.PICTURE_TYPE_PNG);ClientAnchor anchor = new XSSFClientAnchor(0, 0, 1023, 255, signatureCol, targetRowIndex, signatureCol + 1, targetRowIndex + 1);drawing.createPicture(anchor, pictureIdx);}}

2、实体类和自定义转换类

//使用自定义转换器,写入空内容,但表头显示“签名”@ExcelProperty(value = "处理人签名", converter = ImageHeaderConverter.class, index = 15)private byte[] signImage;// 使用自定义转换器,写入空内容,但表头显示“签名”@ExcelProperty(value = "处理人签章", converter = ImageHeaderConverter.class, index = 16)private byte[] signatureImage;/*** 自定义Converter*/
public class ImageHeaderConverter implements Converter<byte[]> {@Overridepublic Class supportJavaTypeKey() {return byte[].class;}@Overridepublic WriteCellData<?> convertToExcelData(byte[] value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {// 返回空数据,防止自动转换报错,但表头信息仍会根据@ExcelProperty的value显示return new WriteCellData<>("");}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return null;}public byte[] convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {return new byte[0];}
}

3、根据图片路径下载图片工具类

public class ImagesUtils {public static byte[] downloadImage(String imageUrl) {if (imageUrl == null || imageUrl.trim().isEmpty()) {log.warn("下载图片失败: imageUrl 为空");return null;}HttpURLConnection connection = null;try {URL url = new URL(imageUrl);connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);  // 增加超时时间到10秒connection.setReadTimeout(10000);connection.setInstanceFollowRedirects(true); // 允许自动重定向// 检查 HTTP 响应码int statusCode = connection.getResponseCode();if (statusCode != HttpURLConnection.HTTP_OK) {log.warn("下载图片失败: {},HTTP状态码: {},响应消息: {}",imageUrl, statusCode, connection.getResponseMessage());return null;}try (InputStream inputStream = connection.getInputStream();ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {byte[] buffer = new byte[4096];  // 增大缓冲区提升读取效率int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}return outputStream.toByteArray();}} catch (Exception e) {log.warn("下载图片失败: {},错误类型: {},详细信息: {}",imageUrl, e.getClass().getSimpleName(), e.getMessage(), e); // 输出完整异常return null;} finally {if (connection != null) {connection.disconnect(); // 显式断开连接}}}
}

4、业务逻辑实现

// 1. 查询 Channel_SIGN_SIGNATURE 表,获取所有处理记录List<ChannelSignSignature> processList = channelSignSignatureMapper.selectList(new LambdaQueryWrapper<ChannelSignSignature>().in(ChannelSignSignature::getDisposeChannelId, ids));if (CollectionUtils.isEmpty(processList)) {return ReposeResult.failed(ResultCode.CHANNEL_EXPORT_NULL);}//根据前端传入的ids顺序排序处理记录Map<Long, ChannelSignSignature> processMap = processList.stream().collect(Collectors.toMap(ChannelSignSignature::getDisposeChannelId, Function.identity()));List<ChannelSignSignature> sortedProcessList =  new ArrayList<>();for (Long id :ids) {if (!processMap.containsKey(id)) {return ReposeResult.failed("部分导出ID未保存处理记录,请先处理记录再导出");}sortedProcessList.add(processMap.get(id));}// 2. 获取所有处理人姓名(保持原顺序)List<String> userNames = sortedProcessList.stream().map(ChannelSignSignature::getChannelRecordName).collect(Collectors.toList());// 3. 批量查询用户信息(userId 映射表)Map<String, Long> userIdMap;if (CollectionUtils.isEmpty(userNames)) {userIdMap = new HashMap<>();} else {List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>().in(User::getUsername, userNames));userIdMap = users.stream().collect(Collectors.toMap(User::getUsername, User::getId, (u1, u2) -> u1));}// 4. 查询用户签名 & 签章Map<Long, List<String>> signMap = new HashMap<>();Map<Long, List<String>> signatureMap = new HashMap<>();if (!userIdMap.isEmpty()) {// 4.1 批量查询用户签名(userId -> 签名列表)List<UserSign> userSigns = userSignMapper.selectList(new LambdaQueryWrapper<UserSign>().in(UserSign::getUserId, userIdMap.values()));signMap = userSigns.stream().collect(Collectors.groupingBy(UserSign::getUserId,LinkedHashMap::new,Collectors.mapping(UserSign::getContent, Collectors.toList())));// 4.2 批量查询用户签章(userId -> 签章列表)List<UserSignature> userSignatures = userSignatureMapper.selectList(new LambdaQueryWrapper<UserSignature>().in(UserSignature::getUserId, userIdMap.values()));signatureMap = userSignatures.stream().collect(Collectors.groupingBy(UserSignature::getUserId,LinkedHashMap::new,Collectors.mapping(UserSignature::getContent, Collectors.toList())));}Integer grading = UserUtils.getUser().getGrading();HashMap<Integer, String[]> dropDownMap = new HashMap<>();String[] results = Arrays.stream(ChannelResultEnum.values()).map(ChannelResultEnum::getDesc).toArray(String[]::new);dropDownMap.put(13, results);List<ChannelVO> channels = channelMapper.selectJoinList(ChannelVO.class, new MPJLambdaWrapper<>(DataChannel.class).selectAll(DataChannel.class).select(Dept::getDeptName).selectAs(Period::getDescription, "period").select(BenchInfo::getBench).selectCollection(MachineInfo.class, ChannelVO::getMachineInfos).leftJoin(ChannelModel.class, ChannelModel::getChannelId, DataChannel::getId).leftJoin(MachineInfo.class, MachineInfo::getId, ChannelModel::getModelId).leftJoin(Dept.class, Dept::getId, DataChannel::getDeptId).leftJoin(Period.class, Period::getId, DataChannel::getPeriodId).leftJoin(BenchInfo.class, BenchInfo::getId, DataChannel::getBenchId).ge(ObjectUtils.isNotEmpty(grading), DataChannel::getGrading, grading).in(DataChannel::getId, ids));channels.forEach(channelVO -> {List<String> models = channelVO.getMachineInfos().stream().map(MachineInfo::getModel).collect(Collectors.toList());channelVO.setModel(String.join(",", models));});//根据前端传入的ids顺序排序channelVOMap<Long, ChannelVO> channelVoMap= channels.stream().collect(Collectors.toMap(ChannelVO::getId, Function.identity()));List<ChannelVO> sortedChannelVOs = new ArrayList<>();for (Long id:ids) {if (!channelVoMap.containsKey(id)) {return ReposeResult.failed("部分导出ID未保存检查记录,请先保存处理记录再导出");}sortedChannelVOs.add(channelVoMap.get(id));}List<ExportChannelDTO> exportChannelDTOS = CopyUtils.copyList(sortedChannelVOs, ExportChannelDTO.class);// 5. 按 `userNames` 生成 `signUrls` 和 `signatureUrls`(保证顺序)List<String> signUrls = new ArrayList<>();List<String> signatureUrls = new ArrayList<>();for (String userName : userNames) {Long userId = userIdMap.get(userName);if (userId != null) {List<String> signs = signMap.getOrDefault(userId, Collections.emptyList());List<String> signatures = signatureMap.getOrDefault(userId, Collections.emptyList());// 如果有签名,添加签名;如果有签章,添加签章;如果都有,则都添加if (!signs.isEmpty()) {signUrls.addAll(signs);} else {signUrls.add(null);}if (!signatures.isEmpty()) {signatureUrls.addAll(signatures);} else {signatureUrls.add(null);}} else {// 处理人无签名/签章,则填充 nullsignUrls.add(null);signatureUrls.add(null);}}// 7. 赋值到 DTOfor (int i = 0; i <  exportChannelDTOS.size(); i++) {ExportChannelDTO dto =  exportChannelDTOS.get(i);// 取签名图片if (signUrls.get(i) != null) {dto.setSignImage(downloadImage(BASE_IMAGE_URL + signUrls.get(i)));} else {dto.setSignImage(null);}// 取签章图片if (signatureUrls.get(i) != null) {dto.setSignatureImage(downloadImage(BASE_IMAGE_URL + signatureUrls.get(i)));} else {dto.setSignatureImage(null);}}// 8. 构造图片数据列表,与 checkDTOS 顺序一致List<byte[][]> imagesList = exportChannelDTOS.stream().map(dto -> new byte[][]{dto.getSignImage() != null ? dto.getSignImage() : new byte[0],dto.getSignatureImage() != null ? dto.getSignatureImage() : new byte[0]}).collect(Collectors.toList());ExcelExportUtil.exportExcel(response,exportChannelDTOS,ExportChannelDTO.class,ConstantUtil.DATA_CHANNEL,new DropDownHandler(dropDownMap),imagesList,15,16);log.info("---导出数据通道信息完成---");         

代码不可直接使用,只是自己记录一下,方便下次使用,你完全可以使用AI来帮你写这些部分。

http://www.dtcms.com/wzjs/40310.html

相关文章:

  • 做3个网站需要多大的服务器手游推广加盟
  • 深圳龙岗做网站的公司哪家好网络营销产品概念
  • 更改网站备案负责人设计网站接单
  • 个人网站一年多少钱windows7优化大师官方下载
  • 免费域名注册网站源码seo优化一般包括哪些内容()
  • .net做的网站怎么样高端营销型网站
  • 做网站推广每天加班郑州seo顾问培训
  • 成都网站建设公司高新接外包项目的网站
  • 如何用word做简单的网站宁波网站推广优化
  • 长春火车站是南站还是北站潍坊seo教程
  • 湖南省新邵县建设局网站网页seo搜索引擎优化
  • 鞍山 中企动力提供网站建设网络营销的主要方式
  • 房地产中介网站建设网站排名系统
  • 长沙微信公众号郑州seo建站
  • 买建筑公司网站seo外包软件
  • 南宁网站建设升上去清理优化大师
  • html5手机网站开发视频剪辑培训机构
  • 防网站模板推广普通话标语
  • 进出口贸易网泽成杭州seo网站推广排名
  • 懒人免费建站模板餐饮营销策划方案
  • 视觉设计与制作seo刷关键词排名免费
  • 万州做网站多少钱免费的网站关键词查询工具
  • 一级域名网站怎样收费的模板下载网站
  • wordpress quick chat百度运营优化师
  • 呼和浩特建设委员会网站seo查询外链
  • 景区网站怎么做2023推广平台
  • 网站开发体会800字郑州网络推广方案
  • 网站建设属于哪个税收服务编码找关键词的方法与技巧
  • 大型网站建设公司沈阳百度快速排名平台
  • 响应式网站建设服务提供商国内最新新闻事件今天