1、需求描述
首先系统中有录入电子签章的功能,录入电子签章可以通过 web 端和 uniapp 端进行录入;录入后在业务表中需要导出,导出时有可能要在一个单元格显示多张图片。
2、实现思路
由于之前在 web 端和 app 端实现的电子签章不是一个人做的,导致两边存的数据格式不一样,web 端的存的是 svg 格式的base64 图片,而uniapp 里面存的是 png 格式,导致导出的时候出现了很多问题,我这里把web端的签章改成了 png 格式,这样导出方便许多

image.png
3、代码
/**
* 导出处理电子签章
*
* @param modelMap
* @param templateExportParams
* @param list
* @param response
*/
private void dealSign(ModelMap modelMap, TemplateExportParams templateExportParams, List<Entity> list, HttpServletResponse response) {
OutputStream out = null;
try {
// 拿到模板文件
String filePath = "your_excel_file";
FileInputStream tps = new FileInputStream(new File(filePath));
final HSSFWorkbook workbook = new HSSFWorkbook(tps);
out = response.getOutputStream();
response.reset();
String fileName = "数据";
fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xls");
response.setContentType("APPLICATION/msexcel");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
HSSFSheet sheet = workbook.getSheetAt(0);
HSSFCellStyle style = workbook.createCellStyle();
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
//Font font = workbook.createFont();
//font.setFontHeightInPoints((short) 12);
//style.setFont(font);
style.setWrapText(true);// 设置自动换行
// 全局唯一
Drawing drawing = sheet.createDrawingPatriarch();
for (int i = 0; i < list.size(); i++) {
TbHiddenDangerHandleEntity t = list.get(i);
Row row = sheet.createRow(i + 2); // 前两行被标题和字段占用
// 自己获取到 要用的电子签章
String[] signs= t.getSigns();
if (signs != null) {
// 在最后一列插入图片
Cell rowCell = row.createCell(1);
rowCell.setCellStyle(style);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BufferedImage bufferedImage = ImageUtil.base64ToBufferedImage(signs);
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
// 这里调整的宽高是自己测试的结果,可能和我们的模板有关
sheet.setColumnWidth(1, width * 8 );
row.setHeightInPoints((short) height / 6);
ImageIO.write(bufferedImage, "png", outputStream);
anchor = new HSSFClientAnchor(0, 0, 1023, 255, (short) 1, i + 2, (short) 2, i + 3);
anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);
//画图的顶级管理器,一个sheet只能获取一个(一定要注意这点)
Picture picture = drawing.createPicture(anchor, workbook.addPicture(outputStream.toByteArray(), Workbook.PICTURE_TYPE_PNG));
picture.resize(0.2);
//sheet.autoSizeColumn(25);
}
}
HSSFWorkbook realWorkbook = workbook;
realWorkbook.write(out);
} catch (final Exception e) {
e.printStackTrace();
logger.error(e);
} finally {
try {
response.flushBuffer();
if (out != null) {
out.flush();
out.close();
}
} catch (final IOException e) {
logger.error(e);
}
}
}
- 图片处理工具类
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;
/**
* @author lucien
* @date 2023/7/22
* @dec 描述
*/
public class ImageUtil {
/**
* BufferedImage 编码转换为 base64
* @param bufferedImage
* @return
*/
public static String BufferedImageToBase64(BufferedImage bufferedImage) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
//String imageFormat = getImageFormat(bufferedImage);
try {
ImageIO.write(bufferedImage, "png", baos);//写入流中
} catch (IOException e) {
e.printStackTrace();
}
byte[] bytes = baos.toByteArray();//转换成字节
return "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes);
}
// Method to get the image format (e.g., "png", "jpeg", "jpg") from a BufferedImage
private static String getImageFormat(BufferedImage image) {
for (String format : ImageIO.getWriterFormatNames()) {
if (format.equalsIgnoreCase("png") || format.equalsIgnoreCase("jpeg") || format.equalsIgnoreCase("jpg")) {
if (ImageIO.getImageWritersByFormatName(format).next().getDefaultWriteParam().canWriteCompressed()) {
return format;
}
}
}
return "png"; // Default to PNG format if no suitable format is found
}
/**
* base64 编码转换为 BufferedImage
* @param base64
* @return
*/
public static BufferedImage base64ToBufferedImage(String base64) {
String base64Image = "";
try {
// 弃用 svg
//if(base64.contains("svg")){
// base64Image = base64.replace("image/svg+xml;base64,", "");
// //Base64.Decoder decoder = Base64.getDecoder();
// byte[] svgBytes = java.util.Base64.getDecoder().decode(base64Image);
// // Use Apache Batik to convert SVG to BufferedImage
// Transcoder transcoder = new PNGTranscoder();
// TranscoderInput transcoderInput = new TranscoderInput(new ByteArrayInputStream(svgBytes));
// ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// TranscoderOutput transcoderOutput = new TranscoderOutput(outputStream);
// transcoder.transcode(transcoderInput, transcoderOutput);
//
// // Convert the output stream to a BufferedImage
// ByteArrayInputStream imageInputStream = new ByteArrayInputStream(outputStream.toByteArray());
// return ImageIO.read(imageInputStream);
//} else if(base64.contains("image/png;base64,")){
base64Image = base64.replace("data:image/png;base64,", "").replace("image/png;base64,", "");
// 这是从 app 录入的签章,先缩小
byte[] bytes1 = java.util.Base64.getDecoder().decode(base64Image);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
BufferedImage read = ImageIO.read(bais);
//return scale(read,0.3f);
return read;
//}
} catch (IOException e) {
e.printStackTrace();
}
//catch (TranscoderException e) {
// e.printStackTrace();
//}
return null;
}
/**
* 将多个 base64 图片合并转换为 BufferedImage
* @param base64Str
* @return
*/
public static BufferedImage base64ToBufferedImage(ArrayList<String> base64Str) {
if(base64Str.size() == 0){
return null;
}else if(base64Str.size() == 1){
return base64ToBufferedImage(base64Str.get(0));
} else {
try {
return combineImages(base64Str);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
// Method to combine multiple images into a single image
private static BufferedImage combineImages(ArrayList<String> base64Str) throws IOException {
int totalWidth = 0;
int maxHeight = 0;
// Find the total width and maximum height of the images
for (String imageBase64 : base64Str) {
BufferedImage image = base64ToBufferedImage(imageBase64);
totalWidth += image.getWidth();
maxHeight = Math.max(maxHeight, image.getHeight());
}
// Create a new combined image with the calculated dimensions
BufferedImage combinedImage = new BufferedImage(totalWidth, maxHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = combinedImage.createGraphics();
int xOffset = 0;
for (String imageBase64 : base64Str) {
BufferedImage image = base64ToBufferedImage(imageBase64);
g.drawImage(image, xOffset, 0, null);
xOffset += image.getWidth();
}
g.dispose();
// 缩放一半
//return scale(combinedImage,0.1f);
return combinedImage;
}
// 缩放图片,经过测试,有bug
//public static BufferedImage scale(BufferedImage srcImageBuffer, float scale){
// //获取缩放后的宽高
// int width = (int) (srcImageBuffer.getWidth()*scale);
// int height = (int) (srcImageBuffer.getHeight()*scale);
// //调用缩放方法获取缩放后的图片
// Image img = srcImageBuffer.getScaledInstance(width , height, Image.SCALE_DEFAULT);
// //创建一个新的缓存图片
// BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// //获取画笔
// Graphics2D graphics = image.createGraphics();
// graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// //将Image对象画在画布上,最后一个参数,ImageObserver:接收有关 Image 信息通知的异步更新接口,没用到直接传空
// graphics.drawImage(img, 0, 0,width,height,null);
//
// //一定要释放资源
// graphics.dispose();
// //获取到文件的后缀名
// return image;
//
//}
}
- 注意:如果一定要用 svg 格式的图片,要用到 下面的 依赖来处理
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.7</version>
</dependency>
-
最后的效果图:
image.png
4、总结
这里实现的时候还是遇到了一些问题的,最开始因为 web 端和 app 端生成的 电子签章不一样,导致导出的时候遇到了各种各样的问题,最后还是把base64 统一成 png 格式,这样处理起来好很多
另外,由于app生成的电子签章比例和web端生成的不一样,app生成的偏大,导致导出的时候大小不协调,如果想看怎么处理的,可以看我这篇博客:
uniapp 电子签章功能实现 + 保存时旋转90度(横向) + 保存时缩小图片 - 简书 (jianshu.com)
