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.rhkb.cn/news/230578.html

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

相关文章

vue-打包

打包的作用 说明&#xff1a;vue脚手架只是开发过程中&#xff0c;协助开发的工具&#xff0c;当真正开发完了>脚手架不参与上线 打包的作用&#xff1a; 1&#xff09;将多个文件压缩合并成一个文件 2&#xff09;语法降级 3&#xff09;less sass ts语法解析 打包后…

liunx操作系统基础及进阶

一、基础入门 1、Linux系统简介 什么是Liunx&#xff1f; Linux在设计之初&#xff0c;是一个基于POSIX的多用户、多任务并且支持多线程和多CPU的操作系统&#xff0c;它是由世界各地成千上万的程序员设计和开发实现&#xff1b; 在当今社会&#xff0c;Linux 系统主要被应…

【智慧零售】东胜物联蓝牙网关硬件解决方案,促进零售门店数字化管理

依托物联网&#xff08;IoT&#xff09;、大数据、人工智能&#xff08;AI&#xff09;等快速发展&#xff0c;数字化和智能化已成为零售企业的核心竞争力。更多的企业通过引入人工智能、大数据等先进技术手段&#xff0c;提高门店运营效率和服务质量。 某连锁咖啡企业牢牢抓住…

[嵌入式C][入门篇] 快速掌握基础(9个语句)

开发环境&#xff1a; 网页版&#xff1a;跳转本地开发(Vscode)&#xff1a;跳转 文章目录 一、基础语法&#xff08;1&#xff09;if (如果)示例1: 普通使用 if示例2: 带否则 else示例3: 否则如果 else if &#xff08;2&#xff09;switch case (选择)规则示例1: &#xff0…

谷歌浏览器 模拟定位

注意事项&#xff1a; 如果要清除位置信息&#xff0c;需将Geolocation修改为No override模拟定位之后需要刷新页面&#xff0c;网页才会生效如果模拟定位&#xff0c;一段时间没有操作&#xff0c;就会清空模拟定位&#xff0c;类似于No override

ubuntu远程桌面连接之novnc

一、前言 该操作是为了实现vnc桌面连接为url连接方式&#xff0c;且在浏览器中可以对ubuntu进行操作。在使用novnc进行操作前&#xff0c;需要先安装vnc才可。ubuntu下如何安装vnc&#xff0c;可看博主前面写的一篇文&#xff0c;ubuntu远程桌面连接之vnc-CSDN博客&#xff0c;…

案例074:基于微信小程序的儿童预防接种预约管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder …

物流实时数仓:数仓搭建(DWS)一

系列文章目录 物流实时数仓&#xff1a;采集通道搭建 物流实时数仓&#xff1a;数仓搭建 物流实时数仓&#xff1a;数仓搭建&#xff08;DIM&#xff09; 物流实时数仓&#xff1a;数仓搭建&#xff08;DWD&#xff09;一 物流实时数仓&#xff1a;数仓搭建&#xff08;DWD&am…

22款奔驰GLE450升级香氛负离子 车载香薰

相信大家都知道&#xff0c;奔驰自从研发出香氛负离子系统后&#xff0c;一直都受广大奔驰车主的追捧&#xff0c;香氛负离子不仅可以散发出清香淡雅的香气外&#xff0c;还可以对车内的空气进行过滤&#xff0c;使车内的有害气味通过负离子进行过滤&#xff0c;达到车内保持清…

神经网络:经典模型热门模型

在这里插入代码片【一】目标检测中IOU的相关概念与计算 IoU&#xff08;Intersection over Union&#xff09;即交并比&#xff0c;是目标检测任务中一个重要的模块&#xff0c;其是GT bbox与pred bbox交集的面积 / 二者并集的面积。 下面我们用坐标&#xff08;top&#xff0…

Oracle导出CSV文件

利用spool spool基本格式&#xff1a; spool 路径文件名 select col1||,||col2||,||col3||,||col4 from tablename; spool off spool常用的设置&#xff1a; set colsep ;    //域输出分隔符 set echo off;    //显示start启动的脚本中的每个sql命令&#xff0c;缺…

ROS学习记录:在ROS中用C++实现激光雷达避障

前言 本文建立在成功获取激光雷达数据的基础上&#xff0c;详细参考 在ROS中用C实现获取激光雷达的数据 一、实现思路 二、在VScode中打开之前编写好的lidar_node.cpp 三、在lidar_node.cpp中写入如下代码 #include <ros/ros.h> #include <std_msgs/String.h> …

k8s---pod的生命周期

pod的相关知识 pod是k8s中最小的资源管理组件 pod也是最小化运行容器化的应用的资源管理对象 pod是一个抽象的概念&#xff0c;可以理解为一个或者多个容器化应用的集合。 k8s中pod的两种使用方式 &#xff08;1&#xff09;一个pod中运行一个容器。"每个po中一个容器&…

知虾会员**成为知虾会员,尊享专属权益**

在当今繁忙的生活中&#xff0c;线上购物已经成为现代人们的主要消费方式之一。而作为线上购物平台的领军者之一&#xff0c;Shopee为了提供更加个性化和便利的购物体验&#xff0c;推出了知虾会员&#xff08;Shopee会员&#xff09;服务。知虾会员不仅可以享受到一系列会员专…

国产化软硬件升级之路:πDataCS 赋能工业软件创新与实践

在国产化浪潮的推动下&#xff0c;基础设施软硬件替换和升级的需求日益增长。全栈国产化软硬件升级替换已成为许多领域中的必选项&#xff0c;也引起了数据库和存储领域的广泛关注。近年来&#xff0c;虽然涌现了许多成功的替换案例&#xff0c;但仍然面临着一些问题。 数据库…

某音关键词搜索商品接口,某音关键词搜索商品列表接口,宝贝详情页接口,某音商品比价接口接入方案

要接入API接口以采集电商平台上的商品数据&#xff0c;可以按照以下步骤进行&#xff1a; 1、找到可用的API接口&#xff1a;首先&#xff0c;需要找到支持查询商品信息的API接口。这些信息通常可以在电商平台的官方文档或开发者门户网站上找到。 2、注册并获取API密钥&#x…

界面控件DevExpress Blazor Grid v23.2 - 支持全新的单元格编辑模式

DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验&#xff0c;这个UI自建库提供了一套全面的原生Blazor UI组件&#xff08;包括Pivot Grid、调度程序、图表、数据编辑器和报表等&#xff09;。 在这篇文章中&#xff0c;我们将介…

Golang 通用代码生成器仙童已发布 2.4.0 电音仙女尝鲜版三及其介绍视频,详细介绍了 Oracle 代码生成

Golang 通用代码生成器仙童已发布 2.4.0 电音仙女尝鲜版三及其介绍视频&#xff0c;详细介绍了 Oracle 代码生成 Golang 通用代码生成器仙童已发布 2.4.0 电音仙女尝鲜版三及其介绍视频。详细介绍了 Oracle 代码生成。即生成后端数据库为 Oracle 的 golang web 代码。并同时生…

解决SyntaxError: future feature annotations is not defined,可适用其他包

方法&#xff1a;对报错的包进行降级 pip install tikzplotlib0.9.8site-packages后面是使用pip install安装的包&#xff0c;根据这个找到报错的包 想法来源&#xff1a; 环境是python3.6&#xff0c;完全按照作者要求进行环境配置&#xff0c;但仍报错。 我在网上找的解决…

使用Redis进行搜索

文章目录 构建反向索引 构建反向索引 在Begin-End区域编写 tokenize(content) 函数&#xff0c;实现文本标记化的功能&#xff0c;具体参数与要求如下&#xff1a; 方法参数 content 为待标记化的文本&#xff1b; 文本标记的实现&#xff1a;使用正则表达式提取全小写化后的…