|
|
@@ -0,0 +1,530 @@
|
|
|
+package com.grapecity.controller;
|
|
|
+
|
|
|
+
|
|
|
+import com.alibaba.fastjson2.*;
|
|
|
+import com.grapecity.documents.excel.*;
|
|
|
+import com.grapecity.documents.excel.drawing.ImageType;
|
|
|
+import com.grapecity.documents.excel.template.DataSource.JsonDataSource;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+import java.awt.*;
|
|
|
+import java.awt.Color;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.util.*;
|
|
|
+import java.util.List;
|
|
|
+/**
|
|
|
+ * GrapeCity 控制器
|
|
|
+ * 提供 Excel 模板处理和 PDF 生成功能
|
|
|
+ */
|
|
|
+@RestController
|
|
|
+@RequestMapping("/grapecity")
|
|
|
+public class GrapeCityController {
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(GrapeCityController.class);
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取 Excel 模板中所有图片路径
|
|
|
+ * 遍历工作表中的所有单元格,收集以 .png 或 .jpg 结尾的路径值
|
|
|
+ *
|
|
|
+ * @return JSON 对象,包含图片路径数组和总数
|
|
|
+ * @throws Exception 处理过程中可能抛出的异常
|
|
|
+ */
|
|
|
+ @PostMapping("filePath")
|
|
|
+ public JSONObject filePath(@RequestBody Map<String, Object> request) throws Exception {
|
|
|
+ if (request == null) {
|
|
|
+ throw new IllegalArgumentException("请求参数不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.debug("收到 filePath 请求:{}", request);
|
|
|
+
|
|
|
+ // 从 JSON 中获取 templateBytes,避免反序列化为 ArrayList
|
|
|
+ byte[] templateBytesArray = convertToByteArray(request.get("templateBytes"), "templateBytes");
|
|
|
+ Map<String, Object> data = extractDataMap(request.get("data"));
|
|
|
+
|
|
|
+ Set<String> imagePaths = new HashSet<>();
|
|
|
+ Workbook workbook = loadWorkbook(templateBytesArray, data);
|
|
|
+
|
|
|
+ for (int i = 0; i < workbook.getWorksheets().getCount(); i++) {
|
|
|
+ IWorksheet worksheet = workbook.getWorksheets().get(i);
|
|
|
+ collectImagePaths(worksheet, imagePaths);
|
|
|
+ }
|
|
|
+
|
|
|
+ return new JSONObject().fluentPut("paths", imagePaths);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载工作簿并设置数据源
|
|
|
+ *
|
|
|
+ * @param templateBytes Excel 模板文件的字节数组
|
|
|
+ * @param data 填充数据
|
|
|
+ * @return 加载后的 Workbook 对象
|
|
|
+ * @throws Exception 加载失败时抛出异常
|
|
|
+ */
|
|
|
+ private Workbook loadWorkbook(byte[] templateBytes, Map<String, Object> data) throws Exception {
|
|
|
+ if (templateBytes == null || templateBytes.length == 0) {
|
|
|
+ throw new IllegalArgumentException("模板字节数组不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ WorkbookOptions workbookOptions = new WorkbookOptions();
|
|
|
+ workbookOptions.setPixelBasedColumnWidth(true);
|
|
|
+ XlsxOpenOptions options = new XlsxOpenOptions();
|
|
|
+ options.setImportFlags(EnumSet.of(ImportFlags.Data));
|
|
|
+ options.setDoNotAutoFitAfterOpened(true);
|
|
|
+
|
|
|
+ Workbook workbook = new Workbook(workbookOptions);
|
|
|
+ try (InputStream inputStream = new ByteArrayInputStream(templateBytes)) {
|
|
|
+ workbook.open(inputStream, OpenFileFormat.Sjs);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < workbook.getWorksheets().getCount(); i++) {
|
|
|
+ IWorksheet worksheet = workbook.getWorksheets().get(i);
|
|
|
+ setupWorksheet(worksheet, data);
|
|
|
+ }
|
|
|
+
|
|
|
+ return workbook;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置工作表配置和数据源
|
|
|
+ * 包括页面设置(纸张大小、边距等)和数据绑定
|
|
|
+ *
|
|
|
+ * @param worksheet 要设置的工作表
|
|
|
+ * @param data 填充数据
|
|
|
+ */
|
|
|
+ private void setupWorksheet(IWorksheet worksheet, Map<String, Object> data) {
|
|
|
+ worksheet.getPageSetup().setPrintHeadings(false);
|
|
|
+ worksheet.getPageSetup().setPaperSize(PaperSize.A4);
|
|
|
+ worksheet.getPageSetup().setLeftMargin(7);
|
|
|
+ worksheet.getPageSetup().setRightMargin(7);
|
|
|
+ worksheet.getPageSetup().setCenterHorizontally(true);
|
|
|
+ worksheet.setDataSource(new JsonDataSource(JSON.toJSONString(data)));
|
|
|
+
|
|
|
+ if (!worksheet.getName().contains("封面") && !worksheet.getName().contains("注意")) {
|
|
|
+ worksheet.getPageSetup().setIsAutoFirstPageNumber(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收集工作表中的所有图片路径
|
|
|
+ * 遍历所有单元格,查找值为图片路径(.png 或 .jpg)的单元格
|
|
|
+ *
|
|
|
+ * @param worksheet 要遍历的工作表
|
|
|
+ * @param imagePaths 用于存储找到的图片路径的集合
|
|
|
+ */
|
|
|
+ private void collectImagePaths(IWorksheet worksheet, Set<String> imagePaths) {
|
|
|
+ // 收集单元格中的背景图片路径
|
|
|
+ for (int x = 0; x < worksheet.getRowCount(); x++) {
|
|
|
+ for (int y = 0; y < worksheet.getColumnCount(); y++) {
|
|
|
+ IRange range = worksheet.getCells().get(x, y);
|
|
|
+ Object valueObj = range.getValue();
|
|
|
+ if (range.getBindingPath() != null && valueObj != null) {
|
|
|
+ String value = valueObj.toString();
|
|
|
+ if (value.endsWith(".png") || value.endsWith(".jpg")) {
|
|
|
+ if (value.contains(",")) {
|
|
|
+ String[] paths = value.split(",");
|
|
|
+ for (String path : paths) {
|
|
|
+ String trimmedPath = path.trim();
|
|
|
+ if (!trimmedPath.isEmpty()) {
|
|
|
+ imagePaths.add(trimmedPath);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ imagePaths.add(value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 收集浮动图片路径
|
|
|
+ collectFloatingImages(worksheet, imagePaths);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收集浮动图片路径
|
|
|
+ * 目前主要处理 Illustration 字段中的浮动图片(可选扩展)
|
|
|
+ *
|
|
|
+ * @param worksheet 工作表
|
|
|
+ * @param imagePaths 图片路径集合
|
|
|
+ */
|
|
|
+ private void collectFloatingImages(IWorksheet worksheet, Set<String> imagePaths) {
|
|
|
+ // 这里可以添加收集浮动图片的逻辑,如果需要的话
|
|
|
+ // 目前 pdf 方法中有处理 Illustration 字段,但 filePath 可能不需要
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成完整的 PDF 文件
|
|
|
+ * 将 Excel 模板填充数据后转换为 PDF,支持背景图片、合并图片和浮动图片
|
|
|
+ *
|
|
|
+ * @return 生成的 PDF 文件的字节数组
|
|
|
+ * @throws Exception 处理过程中可能抛出的异常
|
|
|
+ */
|
|
|
+ @PostMapping("pdf")
|
|
|
+ public byte[] fullPdf(@RequestBody Map<String, Object> request) throws Exception {
|
|
|
+ if (request == null) {
|
|
|
+ throw new IllegalArgumentException("请求参数不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.debug("收到 pdf 请求:{}", request);
|
|
|
+
|
|
|
+ // 转换 templateBytes
|
|
|
+ byte[] templateBytes = convertToByteArray(request.get("templateBytes"), "templateBytes");
|
|
|
+ Map<String, Object> data = extractDataMap(request.get("data"));
|
|
|
+
|
|
|
+ // 转换 fileBytes
|
|
|
+ Map<String, byte[]> fileBytes = convertFileBytesMap(request.get("fileBytes"));
|
|
|
+
|
|
|
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
|
+ WorkbookOptions workbookOptions = new WorkbookOptions();
|
|
|
+ workbookOptions.setPixelBasedColumnWidth(true);
|
|
|
+ //Don't autofit row height
|
|
|
+ XlsxOpenOptions options = new XlsxOpenOptions();
|
|
|
+ options.setImportFlags(EnumSet.of(ImportFlags.Data));
|
|
|
+ options.setDoNotAutoFitAfterOpened(true);
|
|
|
+
|
|
|
+ Workbook workbook = null;
|
|
|
+ InputStream inputStream = null;
|
|
|
+ try {
|
|
|
+ workbook = new Workbook(workbookOptions);
|
|
|
+ inputStream = new ByteArrayInputStream(templateBytes);
|
|
|
+ workbook.open(inputStream, OpenFileFormat.Sjs);
|
|
|
+
|
|
|
+ for (int i = 0; i < workbook.getWorksheets().getCount(); i++) {
|
|
|
+ IWorksheet worksheet = workbook.getWorksheets().get(i);
|
|
|
+ worksheet.getPageSetup().setPrintHeadings(false);
|
|
|
+ worksheet.getPageSetup().setPaperSize(PaperSize.A4);
|
|
|
+ worksheet.getPageSetup().setLeftMargin(7); // 左边距
|
|
|
+ worksheet.getPageSetup().setRightMargin(7); // 右边距
|
|
|
+ worksheet.getPageSetup().setCenterHorizontally(true);
|
|
|
+ worksheet.setDataSource(new JsonDataSource(JSON.toJSONString(data)));
|
|
|
+ if (!worksheet.getName().contains("封面") && !worksheet.getName().contains("注意")) {
|
|
|
+ worksheet.getPageSetup().setIsAutoFirstPageNumber(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充签名图片
|
|
|
+ processCellBackgroundImages(worksheet, fileBytes);
|
|
|
+
|
|
|
+ // 浮动图片
|
|
|
+ processFloatingImages(worksheet, data, fileBytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ PrintManager printManager = new PrintManager();
|
|
|
+ //Workbook.FontsFolderPath = this.fontsFolderPath;
|
|
|
+ Workbook.FontProvider = new IFontProvider() {
|
|
|
+ @Override
|
|
|
+ public List<String> getFontFilePaths() {
|
|
|
+ return new ArrayList<>(Arrays.asList(
|
|
|
+ "fonts/simsun.ttf"
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public InputStream getFont(String fontFilePath) {
|
|
|
+ return getClass().getClassLoader().getResourceAsStream(fontFilePath);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ PdfSaveOptions pdfOptions = new PdfSaveOptions();
|
|
|
+ pdfOptions.setIncludeAutoMergedCells(true);
|
|
|
+ List<PageInfo> pages = printManager.paginate(workbook);
|
|
|
+ printManager.savePageInfosToPDF(byteArrayOutputStream, pages, pdfOptions);
|
|
|
+ return byteArrayOutputStream.toByteArray();
|
|
|
+
|
|
|
+ } finally {
|
|
|
+ if (inputStream != null) {
|
|
|
+ try {
|
|
|
+ inputStream.close();
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.warn("关闭输入流失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (byteArrayOutputStream != null) {
|
|
|
+ try {
|
|
|
+ byteArrayOutputStream.close();
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.warn("关闭输出流失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理单元格背景图片
|
|
|
+ */
|
|
|
+ private void processCellBackgroundImages(IWorksheet worksheet, Map<String, byte[]> fileBytes) {
|
|
|
+ for (int x = 0; x < worksheet.getRowCount(); x++) {
|
|
|
+ for (int y = 0; y < worksheet.getColumnCount(); y++) {
|
|
|
+ IRange range = worksheet.getCells().get(x, y);
|
|
|
+ Object valueObj = range.getValue();
|
|
|
+ if (range.getBindingPath() != null && valueObj != null) {
|
|
|
+ String value = valueObj.toString();
|
|
|
+ // 签名图片
|
|
|
+ if (value.endsWith(".png") || value.endsWith(".jpg")) {
|
|
|
+ // 是非多张图片
|
|
|
+ if (!value.contains(",")) {
|
|
|
+ byte[] bytes = fileBytes.get(value);
|
|
|
+ if (bytes != null) {
|
|
|
+ range.setValue(null);
|
|
|
+ IRange mergeArea = range.getMergeArea();
|
|
|
+ mergeArea.setBackgroundImage(bytes);
|
|
|
+ } else {
|
|
|
+ logger.warn("未找到图片数据:{}", value);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ String[] split = value.split(",");
|
|
|
+ byte[][] bytes = new byte[split.length][];
|
|
|
+ for (int k = 0; k < split.length; k++) {
|
|
|
+ bytes[k] = fileBytes.get(split[k].trim());
|
|
|
+ }
|
|
|
+ range.setValue(null);
|
|
|
+ IRange mergeArea = range.getMergeArea();
|
|
|
+
|
|
|
+ // 横向合并图片
|
|
|
+ byte[] mergedImage = mergeImages(bytes);
|
|
|
+ mergeArea.setBackgroundImage(mergedImage);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 复选框
|
|
|
+ if (range.getCellType() instanceof com.grapecity.documents.excel.CheckBoxCellType) {
|
|
|
+ if (!"true".equals(value)) {
|
|
|
+ range.setValue(null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理浮动图片
|
|
|
+ */
|
|
|
+ private void processFloatingImages(IWorksheet worksheet, Map<String, Object> data, Map<String, byte[]> fileBytes) {
|
|
|
+ if (data.get("Illustration") == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Object illustrationObj = data.get("Illustration");
|
|
|
+ String illustration = illustrationObj != null ? illustrationObj.toString() : null;
|
|
|
+
|
|
|
+ if (illustration != null && illustration.startsWith("[") && illustration.endsWith("]")) {
|
|
|
+ JSONArray jsonArray = JSON.parseArray(illustration);
|
|
|
+ List<JSONObject> list = new ArrayList<>();
|
|
|
+
|
|
|
+ for (int k = 0; k < jsonArray.size(); k++) {
|
|
|
+ JSONObject jsonObject = jsonArray.getJSONObject(k);
|
|
|
+ if (jsonObject.getString("sheet").equals(worksheet.getName())) {
|
|
|
+ String url = jsonObject.getString("url");
|
|
|
+ byte[] bytes = fileBytes.get(url);
|
|
|
+ if (bytes != null) {
|
|
|
+ try (InputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
|
|
|
+ worksheet.getShapes().addPictureInPixel(
|
|
|
+ byteArrayInputStream,
|
|
|
+ ImageType.JPG,
|
|
|
+ jsonObject.getDouble("x"),
|
|
|
+ jsonObject.getDouble("y"),
|
|
|
+ jsonObject.getDouble("width"),
|
|
|
+ jsonObject.getDouble("height")
|
|
|
+ );
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.warn("添加浮动图片失败:{}", url, e);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ logger.warn("未找到浮动图片数据:{}", url);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ list.add(jsonObject);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ data.put("Illustration", JSON.toJSONString(list));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 合并图片
|
|
|
+ * 将多张图片合并成一个图片,高度压缩成一致
|
|
|
+ *
|
|
|
+ * @param images 图片数组
|
|
|
+ * @return 合并后的图片
|
|
|
+ */
|
|
|
+ public static byte[] mergeImages(byte[][] images) {
|
|
|
+ if (images == null || images.length == 0) {
|
|
|
+ return new byte[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果只有一张图片且不为空,直接返回
|
|
|
+ if (images.length == 1 && images[0] != null && images[0].length > 0) {
|
|
|
+ return images[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ BufferedImage[] bufferedImages = new BufferedImage[images.length];
|
|
|
+ int totalWidth = 0;
|
|
|
+ int maxHeight = 0;
|
|
|
+
|
|
|
+ // 读取所有图片并计算总宽度和最大高度
|
|
|
+ for (int i = 0; i < images.length; i++) {
|
|
|
+ if (images[i] == null || images[i].length == 0) {
|
|
|
+ logger.warn("跳过空图片索引:{}", i);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bufferedImages[i] = ImageIO.read(new ByteArrayInputStream(images[i]));
|
|
|
+ if (bufferedImages[i] != null) {
|
|
|
+ totalWidth += bufferedImages[i].getWidth();
|
|
|
+ maxHeight = Math.max(maxHeight, bufferedImages[i].getHeight());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (totalWidth == 0 || maxHeight == 0) {
|
|
|
+ logger.warn("没有有效的图片可以合并");
|
|
|
+ return new byte[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新的图片画布
|
|
|
+ BufferedImage mergedImage = new BufferedImage(totalWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
|
|
|
+ Graphics2D g = mergedImage.createGraphics();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 设置白色背景
|
|
|
+ g.setPaint(Color.WHITE);
|
|
|
+ g.fillRect(0, 0, totalWidth, maxHeight);
|
|
|
+
|
|
|
+ // 绘制所有图片
|
|
|
+ int xOffset = 0;
|
|
|
+ for (BufferedImage img : bufferedImages) {
|
|
|
+ if (img == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ int width = img.getWidth();
|
|
|
+ int height = img.getHeight();
|
|
|
+
|
|
|
+ // 按比例缩放图片以适应高度
|
|
|
+ if (height != maxHeight) {
|
|
|
+ double scale = (double) maxHeight / height;
|
|
|
+ int scaledWidth = (int) (width * scale);
|
|
|
+
|
|
|
+ BufferedImage scaledImage = new BufferedImage(scaledWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
|
|
|
+ Graphics2D g2d = scaledImage.createGraphics();
|
|
|
+ try {
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
|
|
+ g2d.drawImage(img, 0, 0, scaledWidth, maxHeight, null);
|
|
|
+ } finally {
|
|
|
+ g2d.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ g.drawImage(scaledImage, xOffset, 0, null);
|
|
|
+ xOffset += scaledWidth;
|
|
|
+ } else {
|
|
|
+ g.drawImage(img, xOffset, 0, null);
|
|
|
+ xOffset += width;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ g.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换为字节数组
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
+ try {
|
|
|
+ ImageIO.write(mergedImage, "jpg", baos);
|
|
|
+ return baos.toByteArray();
|
|
|
+ } finally {
|
|
|
+ baos.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ logger.error("合并图片失败", e);
|
|
|
+ throw new RuntimeException("合并图片失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将 Object 转换为 byte[] 数组
|
|
|
+ * 支持 List<Integer>、List<Byte>、byte[] 等多种格式
|
|
|
+ *
|
|
|
+ * @param obj 待转换的对象
|
|
|
+ * @param paramName 参数名称,用于错误提示
|
|
|
+ * @return 转换后的 byte[] 数组
|
|
|
+ * @throws IllegalArgumentException 当对象格式不正确时抛出
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private byte[] convertToByteArray(Object obj, String paramName) {
|
|
|
+ if (obj == null) {
|
|
|
+ throw new IllegalArgumentException(paramName + " 不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj instanceof byte[]) {
|
|
|
+ return (byte[]) obj;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj instanceof List) {
|
|
|
+ List<?> list = (List<?>) obj;
|
|
|
+ byte[] result = new byte[list.size()];
|
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
|
+ Object item = list.get(i);
|
|
|
+ if (item instanceof Number) {
|
|
|
+ result[i] = ((Number) item).byteValue();
|
|
|
+ } else if (item instanceof Integer) {
|
|
|
+ result[i] = ((Integer) item).byteValue();
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException(paramName + " 中包含非数字元素");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new IllegalArgumentException(paramName + " 必须是 byte[] 或 List<Number> 类型");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从请求中提取 Data Map
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private Map<String, Object> extractDataMap(Object dataObj) {
|
|
|
+ if (dataObj == null) {
|
|
|
+ return new HashMap<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dataObj instanceof Map) {
|
|
|
+ return (Map<String, Object>) dataObj;
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new IllegalArgumentException("data 必须是 Map 类型");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换 fileBytes Map
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private Map<String, byte[]> convertFileBytesMap(Object fileBytesObj) {
|
|
|
+ if (fileBytesObj == null) {
|
|
|
+ return new HashMap<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(fileBytesObj instanceof Map)) {
|
|
|
+ throw new IllegalArgumentException("fileBytes 必须是 Map 类型");
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<?, ?> rawMap = (Map<?, ?>) fileBytesObj;
|
|
|
+ Map<String, byte[]> result = new HashMap<>(rawMap.size());
|
|
|
+
|
|
|
+ for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
|
|
|
+ String key = entry.getKey() != null ? entry.getKey().toString() : null;
|
|
|
+ if (key == null || key.isEmpty()) {
|
|
|
+ logger.warn("跳过空的图片路径 key");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] value = convertToByteArray(entry.getValue(), "fileBytes[" + key + "]");
|
|
|
+ result.put(key, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+}
|