Excel模板填充:从minio上获取模板使用easyExcel填充

最近工作中有个excel导出的功能,要求导出的模板和客户提供的模板一致,而客户提供的模板有着复杂的表头和独特列表风格,像以往使用poi去画是非常耗时间的,比如需要考虑字体大小,单元格合并,单元格的格式等问题;

所有给大家推荐使用easyExcel填充,我们只需要把客户的模板进行简单处理,上传到文件服务器,需要导出的的时候,就从文件服务器拿模板进行填充返回前端,这样非常便捷和效率高。

easyExcel官方文档:填充Excel | Easy Excel

 一、流程展示

我准备了一个简单的模板进行测试

1.1 模板准备

1.2 模板文件上传到minio

1.3 填充后的文件

 

 二、关键代码

1.1 引入依赖minio、easyExcel

            <dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.0</version></dependency>
            <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.0</version></dependency>

 1.2 minio配置及工具类准备

 1.2.1 minio yml文件配置
minio:endpoint: http://xxx.xxx.xxx.xxx:xxxaccesskey: xxxsecretKey: xxxbucketName: xxxexpires: 3
 1.2.2 minio 配置类
package com.ruoyi.common.config;import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MinioUtils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;/*** @author qujingye*/
@Data
@Configuration
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {/*** 地址*/private String endpoint;/*** 桶的名字*/private static String bucketName;/*** 访问key*/private String accessKey;/*** 秘钥key*/private String secretKey;/*** 临时文件的过期时间(天)*/private static Integer expires;@Beanpublic MinioUtils creatMinioClient() {return new MinioUtils(endpoint, bucketName, accessKey, secretKey);}/*** 临时文件的过期时间** @param expires Xml内容*/public void setExpires(Integer expires) {MinioConfig.expires = DateUtils.getDaySecond(expires);}public static Integer getExpires() {return expires;}public static String getBucketName() {return bucketName;}public void setBucketName(String bucketName) {MinioConfig.bucketName = bucketName;}
}
 1.2.3 minio 工具类 
package com.ruoyi.common.utils;import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.common.utils.uuid.Seq;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.*;/*** Minio工具类** @author qujingye*/
public class MinioUtils {private static final Logger log = LoggerFactory.getLogger(MinioUtils.class);private static MinioClient minioClient;private static String endpoint;private static String bucketName;private static String accessKey;private static String secretKey;private static final String SEPARATOR = "/";/*** 默认大小 200M*/public static final long DEFAULT_MAX_SIZE = 200 * 1024 * 1024;/*** 默认的文件名最大长度 100*/public static final int DEFAULT_FILE_NAME_LENGTH = 100;private MinioUtils() {}public MinioUtils(String endpoint, String bucketName, String accessKey, String secretKey) {MinioUtils.endpoint = endpoint;MinioUtils.bucketName = bucketName;MinioUtils.accessKey = accessKey;MinioUtils.secretKey = secretKey;createMinioClient();}/*** 创建minioClient*/public void createMinioClient() {try {if (null == minioClient) {log.info("minioClient create start");minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();createBucket();log.info("minioClient create end");}} catch (Exception e) {log.error("连接MinIO服务器异常:{}", e);}}/*** 获取上传文件的基础路径** @return url*/public static String getBasisUrl() {return endpoint + SEPARATOR + bucketName + SEPARATOR;}/*** 初始化Bucket** @throws Exception 异常*/private static void createBucket()throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException, RegionConflictException {if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/*** 验证bucketName是否存在** @return boolean true:存在*/public static boolean bucketExists()throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/*** 创建bucket** @param bucketName bucket名称*/public static void createBucket(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException, RegionConflictException {if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/*** 获取存储桶策略** @param bucketName 存储桶名称* @return json*/private JSONObject getBucketPolicy(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, BucketPolicyTooLargeException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException {String bucketPolicy = minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());return JSONObject.parseObject(bucketPolicy);}/*** 获取全部bucket* <p>* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets*/public static List<Bucket> getAllBuckets()throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.listBuckets();}/*** 根据bucketName获取信息** @param bucketName bucket名称*/public static Optional<Bucket> getBucket(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}/*** 根据bucketName删除信息** @param bucketName bucket名称*/public static void removeBucket(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}/*** 判断文件是否存在** @param bucketName 存储桶* @param objectName 对象* @return true:存在*/public static boolean doesObjectExist(String bucketName, String objectName) {boolean exist = true;try {minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());} catch (Exception e) {exist = false;}return exist;}/*** 判断文件夹是否存在** @param bucketName 存储桶* @param objectName 文件夹名称(去掉/)* @return true:存在*/public static boolean doesFolderExist(String bucketName, String objectName) {boolean exist = false;try {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());for (Result<Item> result : results) {Item item = result.get();if (item.isDir() && objectName.equals(item.objectName())) {exist = true;}}} catch (Exception e) {exist = false;}return exist;}/*** 根据文件前置查询文件** @param bucketName bucket名称* @param prefix     前缀* @param recursive  是否递归查询* @return MinioItem 列表*/public static List<Item> getAllObjectsByPrefix(String bucketName, String prefix,boolean recursive)throws ErrorResponseException, InsufficientDataException, InternalException, InvalidBucketNameException, InvalidKeyException, InvalidResponseException,IOException, NoSuchAlgorithmException, ServerException, XmlParserException {List<Item> list = new ArrayList<>();Iterable<Result<Item>> objectsIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());if (objectsIterator != null) {for (Result<Item> o : objectsIterator) {Item item = o.get();list.add(item);}}return list;}/*** 获取文件流** @param bucketName bucket名称* @param objectName 文件名称* @return 二进制流*/public static InputStream getObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 断点下载** @param bucketName bucket名称* @param objectName 文件名称* @param offset     起始字节的位置* @param length     要读取的长度* @return 流*/public InputStream getObject(String bucketName, String objectName, long offset, long length)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build());}/*** 获取路径下文件列表** @param bucketName bucket名称* @param prefix     文件名称* @param recursive  是否递归查找,如果是false,就模拟文件夹结构查找* @return 二进制流*/public static Iterable<Result<Item>> listObjects(String bucketName, String prefix,boolean recursive) {return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());}/*** 通过MultipartFile,上传文件** @param bucketName 存储桶* @param file       文件* @param objectName 对象名*/public static ObjectWriteResponse putObject(String bucketName, MultipartFile file,String objectName, String contentType)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {InputStream inputStream = file.getInputStream();return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType).stream(inputStream, inputStream.available(), -1).build());}/*** 上传本地文件** @param bucketName 存储桶* @param objectName 对象名称* @param fileName   本地文件路径*/public static ObjectWriteResponse putObject(String bucketName, String objectName,String fileName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());}/*** 通过流上传文件** @param bucketName  存储桶* @param objectName  文件对象* @param inputStream 文件流*/public static ObjectWriteResponse putObject(String bucketName, String objectName,InputStream inputStream)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, inputStream.available(), -1).build());}/*** 创建文件夹或目录** @param bucketName 存储桶* @param objectName 目录路径*/public static ObjectWriteResponse putDirObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());}/*** 获取文件信息, 如果抛出异常则说明文件不存在** @param bucketName bucket名称* @param objectName 文件名称*/public static ObjectStat statObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 拷贝文件** @param bucketName    bucket名称* @param objectName    文件名称* @param srcBucketName 目标bucket名称* @param srcObjectName 目标文件名称*/public static ObjectWriteResponse copyObject(String bucketName, String objectName,String srcBucketName, String srcObjectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(bucketName).object(objectName).build()).bucket(srcBucketName).object(srcObjectName).build());}/*** 删除文件** @param bucketName bucket名称* @param objectName 文件名称*/public static void removeObject(String bucketName, String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 批量删除文件** @param bucketName bucket* @param keys       需要删除的文件列表* @return*//*public static Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> keys) {List<DeleteObject> objects = new LinkedList<>();keys.forEach(s -> {objects.add(new DeleteObject(s));});return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());}*/public static void removeObjects(String bucketName, List<String> keys) {List<DeleteObject> objects = new LinkedList<>();keys.forEach(s -> {objects.add(new DeleteObject(s));try {removeObject(bucketName, s);} catch (Exception e) {log.error("批量删除失败!error:{}", e);}});}/*** 获取文件外链** @param bucketName bucket名称* @param objectName 文件名称* @param expires    过期时间 <=7 秒级* @return url*/public static String getPresignedObjectUrl(String bucketName, String objectName,Integer expires)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, InvalidExpiresRangeException, ServerException, InternalException, NoSuchAlgorithmException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.presignedGetObject(bucketName, objectName, expires);}/*** 给presigned URL设置策略** @param bucketName 存储桶* @param objectName 对象名* @param expires    过期策略* @return map*/public static Map<String, String> presignedGetObject(String bucketName, String objectName,Integer expires)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, InvalidExpiresRangeException, ServerException, InternalException, NoSuchAlgorithmException, XmlParserException, InvalidBucketNameException, ErrorResponseException {PostPolicy policy = new PostPolicy(bucketName, objectName,ZonedDateTime.now().plusDays(7));policy.setContentType("image/png");return minioClient.presignedPostPolicy(policy);}/*** 获得上传的URL** @param bucketName 存储桶* @param objectName 对象名* @param expires    过期策略(秒)* @return*/public static String presignedPutObject(String bucketName, String objectName, Integer expires) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, InvalidExpiresRangeException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {if (expires == null) {// 7天return minioClient.presignedPutObject(bucketName, objectName);}return minioClient.presignedPutObject(bucketName, objectName, expires);}/*** 将URLDecoder编码转成UTF8** @param str* @return* @throws UnsupportedEncodingException*/public static String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");return URLDecoder.decode(url, "UTF-8");}/*** 获取上传地址并做文件校验** @param file allowedExtension* @return String*/public static Map<String, Object> getUploadPath(MultipartFile file, String basePath, String[] allowedExtension)throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,InvalidExtensionException {int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();if (fileNamelength > MinioUtils.DEFAULT_FILE_NAME_LENGTH) {throw new FileNameLengthLimitExceededException(MinioUtils.DEFAULT_FILE_NAME_LENGTH);}assertAllowed(file, allowedExtension);return extractFilename(file, basePath);}/*** 文件大小校验** @param file 上传的文件* @return* @throws FileSizeLimitExceededException 如果超出最大大小* @throws InvalidExtensionException*/public static final void assertAllowed(MultipartFile file, String[] allowedExtension)throws FileSizeLimitExceededException, InvalidExtensionException{long size = file.getSize();if (size > DEFAULT_MAX_SIZE){throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);}String fileName = file.getOriginalFilename();String extension = getExtension(file);if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)){if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION){throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION){throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION){throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION){throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,fileName);}else{throw new InvalidExtensionException(allowedExtension, extension, fileName);}}}/*** 判断MIME类型是否是允许的MIME类型** @param extension* @param allowedExtension* @return*/public static final boolean isAllowedExtension(String extension, String[] allowedExtension){for (String str : allowedExtension){if (str.equalsIgnoreCase(extension)){return true;}}return false;}/*** 编码文件名*/public static final Map<String, Object> extractFilename(MultipartFile file, String basePath){Map<String, Object> map=new HashMap<>();map.put("uploadPath",StringUtils.format("{}/{}/{}.{}",basePath, DateUtils.datePathMonth(), Seq.getId(Seq.uploadSeqType), getExtension(file)));map.put("newFileName",StringUtils.format("{}.{}", Seq.getId(Seq.uploadSeqType), getExtension(file)));return map;}public static final Map<String, Object> extractFilenameText(String fileName, String basePath){Map<String, Object> map=new HashMap<>();map.put("uploadPath",StringUtils.format("{}/{}/{}.{}",basePath, DateUtils.datePathMonth(), Seq.getId(Seq.uploadSeqType), fileName));map.put("newFileName",StringUtils.format("{}.{}", Seq.getId(Seq.uploadSeqType),fileName));return map;}/*** 获取文件名的后缀** @param file 表单文件* @return 后缀名*/public static final String getExtension(MultipartFile file){String extension = FilenameUtils.getExtension(file.getOriginalFilename());if (StringUtils.isEmpty(extension)){extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));}return extension;}}

 1.3 模板填充导出实现接口

 1.3.1 controller
package com.ruoyi.web.controller.common;import com.ruoyi.system.service.ExcelTemplateDemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;/*** @author qujingye* @Classname ExcelTemplateDemoController* @Description 测试 从minio获取文件模板-easyExcel模板填充* @Date 2023/12/31 12:37*/
@RestController
@RequestMapping("/excelTemplateDemo")
public class ExcelTemplateDemoController {@Autowiredprivate ExcelTemplateDemoService excelTemplateDemoService;@GetMapping("/test")public void exportChangeDetail(String resource, HttpServletResponse response) {excelTemplateDemoService.excelTemplateDemoImport(response);}}
 1.3.2 service
package com.ruoyi.system.service;import javax.servlet.http.HttpServletResponse;/*** @author qujingye* @title ExcelTemplateDemoService* @date 2023/12/31 12:40* @description TODO*/
public interface ExcelTemplateDemoService {void excelTemplateDemoImport(HttpServletResponse response);
}
package com.ruoyi.system.service.impl;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.ruoyi.common.config.MinioConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MinioUtils;
import com.ruoyi.system.domain.ProductInfo;
import com.ruoyi.system.service.ExcelTemplateDemoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;/*** @author qujingye* @Classname ExcelTemplateDemoServiceImpl* @Description TODO* @Date 2023/12/31 12:42*/
@Service
public class ExcelTemplateDemoServiceImpl implements ExcelTemplateDemoService {public static final String EXCEL_TEMPLATE_PATH = "/exportExcelTemplate/商品出库单.xlsx";private static final Logger log = LoggerFactory.getLogger(ExcelTemplateDemoServiceImpl.class);/*** 从minio获取模板进行填充** @param response* @return void*/@Overridepublic void excelTemplateDemoImport(HttpServletResponse response) {//输入流InputStream inputStream = null;ServletOutputStream outputStream = null;ExcelWriter excelWriter = null;List<ProductInfo> result = new ArrayList<>();//组装测试商品列表数据 编号 名称及规格 单位	数量 单价 金额 备注getProductInfoTestData(result);//组装测试其他数据HashMap<String, Object> totalMap = new HashMap<>();getOtheroTestData(totalMap);try {inputStream = MinioUtils.getObject(MinioConfig.getBucketName(), EXCEL_TEMPLATE_PATH);//输出流// 获取文件名并转码
//            String name = URLEncoder.encode("商品出库单.xlsx", "UTF-8");
//            response.setHeader("Content-Disposition", "attachment;filename=" + name);response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");outputStream = response.getOutputStream();// 创建填充配置FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();// 创建写对象excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).build();// 创建Sheet对象WriteSheet sheet = EasyExcel.writerSheet(0).build();sheet.setSheetName("商品出库单");// 多组填充excelexcelWriter.fill(totalMap, sheet);excelWriter.fill(result, fillConfig, sheet);} catch (Exception e) {log.info("导出失败={}", e.getMessage());} finally {if (excelWriter != null) {excelWriter.finish();}//关闭流if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}private void getOtheroTestData(HashMap<String, Object> totalMap) {//客户名称totalMap.put("name","小张");//客户编码totalMap.put("code","001XP");//日期totalMap.put("date", DateUtils.getChineseDate());//总数量totalMap.put("sumNums",20);//合计:totalMap.put("sumMoney",2000);//主管:totalMap.put("governor","王主管");//财务:totalMap.put("finance","李财务");//保管员totalMap.put("guardian","王保管员");//保管员totalMap.put("HandlerName","刘经手人");}private void getProductInfoTestData(List<ProductInfo> result) {result.add(new ProductInfo("001", "商品1", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("002", "商品2", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("003", "商品3", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("004", "商品4", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("005", "商品5", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("006", "商品6", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("007", "商品7", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("008", "商品8", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("009", "商品9", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));result.add(new ProductInfo("010", "商品10", "单位1", 2, BigDecimal.valueOf(100), BigDecimal.valueOf(200), "无"));}
}

📝结束语:

总结不易😄觉得有用的话,还请各位大佬动动发财的小手
给小瞿扣个三连:⛳️ 点赞⭐️收藏 ❤️ 关注
这个对我真的非常重要,拜托啦!
本人水平有限,如有纰漏,欢迎各位大佬评论指正!💞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/312231.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务篇之Nacos快速入门

Nacos 简介 Nacos 起源 Nacos 起源于阿里巴巴 2008 年的五彩石项目&#xff08;完成微服务拆分和业务中台建设&#xff09;&#xff0c;经历了阿里十年双十⼀的洪峰流量的考验&#xff0c;沉淀了简单易用、稳定可靠、性能卓越等核心特性。随着云计算的兴起和受到开源软件行业…

2021-05-08 51单片机74HC164、74LS164、74HCT164、74HC154、74HCT154应用三极管控制继电器

74HC164、74HCT164是8位边沿触发式移位寄存器&#xff0c;串行输入数据&#xff0c;然后并行输出。数据通过两个输入端&#xff08;DSA或DSB&#xff09;之一串行输入&#xff1b;任一输入端可以用作高电平使能端&#xff0c;控制另一输入端的数据输入。两个输入端或者连接在一…

文献综述 AI 应用对比 — Elicit, GPTs 与 Perplexity

&#xff08;注&#xff1a;本文为小报童精选文章&#xff0c;已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费&#xff09; 通过我的这些尝试&#xff0c;你无需再自己去摸索&#xff0c;可以直接根据我展示的结果选择合适的工具&#xff0c;更有效地进行文献回顾。 …

SpringMVC框架

SpringMVC 三层架构MVC模式SpringMVC入门案例总结 三层架构 表现层&#xff08;web&#xff09; 页面数据的收集&#xff0c;产出页面 业务逻辑层&#xff08;service&#xff09; 业务处理 数据访问层&#xff08;Dao&#xff09; 数据持久化 MVC模式 SpringMVC 基于Java…

WorkPlus私有化即时通讯的标杆,助力企业实现信息管控与保障

在信息时代&#xff0c;保护企业的信息安全至关重要。而私有化即时通讯成为了企业提升信息安全的重要手段。作为私有化即时通讯的领先选择&#xff0c;WorkPlus以其卓越的性能和领先的技术&#xff0c;为企业提供了安全可靠的通信解决方案。 私有化即时通讯是企业保护信息安全的…

数据结构:堆的三部曲 (一)堆的实现

堆的实现 1.堆的结构1.1堆的定义理解 2.堆的实现&#xff08;以小根堆为例&#xff09;2.1 堆结构体的定义2.2 堆的插入交换函数向上调整算法插入函数的代码 2.3 堆的删除向下调整算法&#xff1a;删除函数的代码&#xff1a; 2.4其他操作 3.测试以及完整源代码实现3.1测试代码…

redis—List列表

目录 前言 1.常见命令 2.使用场景 前言 列表类型是用来存储多个有序的字符串&#xff0c;如图2-19所示&#xff0c;a、b、C、d、e五个元素从左到右组成 了一个有序的列表&#xff0c;列表中的每个字符串称为元素(element) &#xff0c;一个列表最多可以存储2^32 - 1 个元素…

Spring Cloud Gateway集成Knife4j

1、前提 网关路由能够正常工作。 案例 基于 Spring Cloud Gateway Nacos 实现动态路由拓展的参考地址&#xff1a;Spring Cloud Gateway Nacos 实现动态路由 详细官网案例&#xff1a;https://doc.xiaominfo.com/docs/middleware-sources/spring-cloud-gateway/spring-gatewa…

gitee+picgo+typora图床搭建

giteepicgotypora图床搭建 1.安装typora 官网下载直接安装&#xff1a;https://www.typora.io/#download 2.编辑typora图像设置 打开 文件 -> 偏好设置 -> 图像设置 插入图片时 选择 上传图片设置 上传服务 为 PicGo-Core(command line) 3.为typora安装PicGo-Core 点…

zabbix监控基础

目录 一.zabbix概述 1.zabbix程序结构 2.Zabbix 监控原理 3.zabbix中核心的资源介绍 4.zabbix-agent支持的协议 二.zabbix监控环境搭建 1.zabbix服务器端 1) 基本配置 2) 安装 2.配置zabbix 1) 数据库设置 2) zabbix设置 3) ph…

Halcon利用颜色信息提取背景相似的字符区域

Halcon利用颜色信息提取背景相似的字符区域 文章目录 Halcon利用颜色信息提取背景相似的字符区域 本文以一个例子说明利用颜色信息进行目标提取。如图所示&#xff0c;图&#xff08;a&#xff09;为输入的原始图像,目标为提取出字符区域。由图&#xff08;a&#xff09;可见&a…

记chrome的hackbar无法post php://input的问题

尽管hackbar支持post请求体&#xff0c;但是当请求体里面没有等于号的时候&#xff0c;无法post出去&#xff0c;这样如果需要使用php://input绕过waf的时候就没法做。 在开发人员工具的网络里面可以看到不使用等于号的情况下没有荷载。 之后在这里看到了解决方法&#xff0c;…