注:文章皆为个人纪录,可用性请以最终结果为准,若有错还请大佬们指出,谢谢!


程序入口

package com.jxz.owner.controller.excel;import com.jxz.owner.entity.CsIssue;
import com.jxz.owner.entity.excel.CsIssueExcel;
import com.jxz.owner.sdk.oss.service.IOssService;
import com.jxz.owner.utils.DateTimeUtils;
import com.jxz.owner.utils.ExcelUtils;
import com.sun.istack.internal.NotNull;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** @Description: Excel 文件的导入导出** @Date: 2022/1/5* @Author: jiangXueZhi*/
@RestController(value = "c1") // 当有两个控制器名称相同时做value的区分
@RequestMapping("/api/excel")
public class ExcelController {@Resourceprivate IOssService iOssService;/*** 文件上传获取下载地址,并可解析上传的文件生成一个List<T>** @param file 上传的文件**/@RequestMapping(value = "/import", method = RequestMethod.POST)public void upload(MultipartFile file) {String cloudFile = iOssService.upload(file, "excel", file.getOriginalFilename()); // 相对路径String path = iOssService.getDownLoadUrl(cloudFile); // 绝对路径System.out.println("###########  PATH : " + path);/* 解析文件 */List<CsIssueExcel> csIssues = ExcelUtils.resolveExcel(file, CsIssueExcel.class, null);if (csIssues != null) {System.out.println(csIssues.size());}System.out.println(csIssues);}/*** 下载文件流* * @param response 文件流*/@RequestMapping(value = "/export", method = RequestMethod.POST)public void export(HttpServletResponse response) {List<CsIssue> csIssueList = getExcelDownLoadData(); // 在实体类中 用 @ExcelIgnore 注解控制字段不展示String fileName = ExcelUtils.getFileName("issue");ExcelUtils.downLoad(response, csIssueList, CsIssue.class, fileName);}/*** 下载文件地址** @param response 文件下载地址*/@RequestMapping(value = "/exportStr", method = RequestMethod.POST)public void exportStr(HttpServletResponse response) {List<CsIssue> csIssueList = getExcelDownLoadData(); // 在实体类中 用 @ExcelIgnore 注解控制字段不展示String fileName = ExcelUtils.getFileName("issue");InputStream in = ExcelUtils.downLoadStr(response, csIssueList, CsIssue.class, fileName);String path = iOssService.upload(in, fileName, "excel");System.out.println("###########  PATH : " + path);}@NotNullprivate List<CsIssue> getExcelDownLoadData() {List<CsIssue> csIssueList = new ArrayList<>();for (int i = 0; i < 10; i++) {CsIssue cs = new CsIssue();cs.setId(i);cs.setIssueNo("ino_" + i * i);cs.setAccountNo("ano_" + i * i);cs.setTransactionId("tid_" + i * i);cs.setTransactionDate(DateTimeUtils.getCurrentDate());cs.setInstituteId(i);cs.setOperationType(1);cs.setAmount(i * 100);cs.setPhoneNo("123456");cs.setIssue("这是一个测试案例");cs.setConclusion("这是一个结论案例");cs.setStatus(1);cs.setPicPath("http://xxxx.oss-cn-shanghai.aliyuncs.com/img/106c578df60347e5a78c48f3c1acaeb197.jpg?Expires=1641807443&OSSAccessKeyId=xxxx&Signature=xxxx%3D");cs.setCsInterventionTime(new Date());cs.setCsCompletionTime(new Date());csIssueList.add(cs);}return csIssueList;}
}

导入导出均基于EasyExcel(推荐第一、三种)

依赖如下:

<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.1</version></dependency>

导出(四种方式)

方式一   直接返回流(即输出后直接下载)

步骤一:创建工具类ExcelUtils

package com.jxz.owner.utils;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.jxz.owner.config.ExcelWidthStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;/*** @Description: Excel 相关的工具方法** @Date: 2022/1/12* @Author: jiangXueZhi*/
@Component
@Slf4j
public class ExcelUtils {/*** 每个sheet的容量,即超过时就会把数据分sheet*/private static final int PAGE_SIZE = 10000;/*** 接收一个excel文件,并且进行解析* 注意一旦传入自定义监听器,则返回的list为空,数据需要在自定义监听器里面获取** @param multipartFile excel文件* @param clazz         数据类型的class对象* @param readListener  监听器* @param <T> 泛型* @return 解析的Excel文件数据集合*/public static <T> List<T> resolveExcel(MultipartFile multipartFile, Class<T> clazz, ReadListener<T> readListener) {try (InputStream inputStream = multipartFile.getInputStream()) {return read(inputStream, clazz, readListener);} catch (IOException e) {log.error("解析文件失败..");e.printStackTrace();}return null;}/*** 读文件** @param in 输入流* @param clazz 反射类型* @param readListener 监听器* @param <T> 泛型* @return 解析的Excel文件数据集合*/private static <T> List<T> read(InputStream in, Class<T> clazz, ReadListener<T> readListener) {List<T> list = new ArrayList<>();Optional<ReadListener<T>> optional = Optional.ofNullable(readListener);EasyExcel.read(in, clazz, optional.orElse(new AnalysisEventListener<T>() {@Overridepublic void invoke(T data, AnalysisContext context) {list.add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {log.warn("Parsing is complete");}})).sheet().doRead();return list;}/*** 下载Excel  流 (推荐使用)** @param response 响应体* @param list 数据* @param clazz JavaBean反射对象* @param fileName 文件名 fileName = fileName + "_" + DateTimeUtils.getCurrentDateTime() + ".xlsx";* @param <T> 泛型*/@SuppressWarnings("rawtypes")public static <T> void downLoad(HttpServletResponse response, List<T> list, Class<T> clazz, String fileName) {try {//设置响应的类型setResponse(response, java.net.URLEncoder.encode(fileName, "UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}
//        setResponse(response, fileName);// 头的策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为灰headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.getIndex());// 内容的策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 默认表头HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);// 自适应列宽表头ExcelWidthStyleStrategy excelWidthStyleStrategy = new ExcelWidthStyleStrategy();try (OutputStream outputStream = response.getOutputStream()) {
//            EasyExcelFactory.write(outputStream, clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet("sheet1").doWrite(list);EasyExcelFactory.write(outputStream, clazz).registerWriteHandler(excelWidthStyleStrategy).sheet("sheet1").doWrite(list);} catch (Exception e) {e.printStackTrace();}}/*** 下载Excel  地址 (推荐使用)** @param response 响应体* @param list 数据* @param clazz JavaBean反射对象* @param fileName 文件名 fileName = fileName + "_" + DateTimeUtils.getCurrentDateTime() + ".xlsx";* @param <T> 泛型*/@SuppressWarnings("rawtypes")public static <T> InputStream downLoadStr(HttpServletResponse response, List<T> list, Class<T> clazz, String fileName) {// 单个sheet的容量List<? extends List<?>> lists = splitList(list, PAGE_SIZE);ByteArrayOutputStream os = new ByteArrayOutputStream();try {//设置响应的类型setResponse(response, URLEncoder.encode(fileName, "UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}
//        setResponse(response, fileName);// 头的策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为灰headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.getIndex());// 内容的策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 默认表头HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);// 自适应列宽表头ExcelWidthStyleStrategy excelWidthStyleStrategy = new ExcelWidthStyleStrategy();try (OutputStream outputStream = response.getOutputStream()) {
//            EasyExcelFactory.write(outputStream, clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet("sheet1").doWrite(list);EasyExcelFactory.write(outputStream, clazz).registerWriteHandler(excelWidthStyleStrategy).sheet("sheet1").doWrite(list);
//            ExcelWriter excelWriter = EasyExcel.write(os, clazz).registerWriteHandler(excelWidthStyleStrategy).build();
//            浏览器访问url直接下载文件的方式
//            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(com.example.utils.easyexcel.EasyExcel.formatExcel()).registerWriteHandler(new com.example.utils.easyexcel.EasyExcel.ExcelWidthStyleStrategy()).build();ExcelWriter excelWriter = EasyExcelFactory.write(os, clazz).registerWriteHandler(excelWidthStyleStrategy).build();ExcelWriterSheetBuilder excelWriterSheetBuilder;WriteSheet writeSheet;for (int i = 1; i <= lists.size(); ++i) {excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter);excelWriterSheetBuilder.sheetNo(i).sheetName("sheet" + i);writeSheet = excelWriterSheetBuilder.build();excelWriter.write(lists.get(i - 1), writeSheet);}// 必须要finish才会写入,不finish只会创建empty的文件excelWriter.finish();byte[] content = os.toByteArray();// 返回流文件return new ByteArrayInputStream(content);} catch (Exception e) {e.printStackTrace();}return null;}/*** 切割查询的数据* @param list 需要切割的数据* @param len 按照什么长度切割* @param <T> 泛型* @return 数据*/public static <T> List<List<T>> splitList(List<T> list, int len) {if (list == null || list.size() == 0 || len < 1) {return null;}List<List<T>> result = new ArrayList<List<T>>();int size = list.size();int count = (size + len - 1) / len;for (int i = 0; i < count; i++) {List<T> subList = list.subList(i * len, (Math.min((i + 1) * len, size)));result.add(subList);}return result;}/*** 获取最终的文件名** @param fileName 文件初始名* @return 最终的文件名*/public static String getFileName(String fileName) {return fileName + "_" + DateTimeUtils.getCurrentDateTime() + ".xlsx";}/*** 设置响应体参数** @param response 响应对象* @param fileName 文件名*/public static void setResponse(HttpServletResponse response, String fileName) {response.reset();response.addHeader("content-type", "application/vnd.ms-excel;charset=UTF-8");response.setHeader("Content-Disposition", "attachment;filename=" + fileName);response.addHeader("Cache-Control", "no-cache");}
}

ExcelWidthStyleStrategy      列宽自适应

package com.jxz.owner.config;import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @Description: 列宽自适应** @Date: 2022/1/12* @Author: jiangXueZhi*/
public class ExcelWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {/** 最大列宽 */private static final int MAX_COLUMN_WIDTH = 255;/** 最小列宽 */private static final int MIN_COLUMN_WIDTH = 25;private  final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>(8);@Overrideprotected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head,Integer relativeRowIndex, Boolean isHead) {boolean needSetWidth = isHead || !CollectionUtil.isEmpty(cellDataList);if (!needSetWidth) {return;}Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>(16));Integer columnWidth = dataLength(cellDataList, cell, isHead);if (columnWidth < 0) {return;}if (columnWidth > MAX_COLUMN_WIDTH) {columnWidth = MAX_COLUMN_WIDTH;}if (columnWidth < MIN_COLUMN_WIDTH) {columnWidth = MIN_COLUMN_WIDTH;}Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());if (maxColumnWidth == null || columnWidth > maxColumnWidth) {maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);}}private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {if (isHead) {return cell.getStringCellValue().getBytes().length;}WriteCellData<?> cellData = cellDataList.get(0);CellDataTypeEnum type = cellData.getType();if (type == null) {return -1;}switch (type) {case STRING:return cellData.getStringValue().getBytes().length;case BOOLEAN:return cellData.getBooleanValue().toString().getBytes().length;case NUMBER:return cellData.getNumberValue().toString().getBytes().length;default:return -1;}}
}

步骤二:PostMan测试

不要直接点击 "Send",否则会出现乱码

先点击”Send“右侧的⬆,再选择”Snd and Download“,之后选择下载位置即可


步骤三:Excel展示(单sheet)

显示效果一切正常,图中显示整体缩小是因为为了方便大家看到全局而手动缩小

直接下载流的Excel展示

单sheet

方式二   返回下载地址(上传至"七牛"文件服务器)

步骤一:导入依赖

<!--七牛文件上传sdk--><dependency><groupId>com.qiniu</groupId><artifactId>qiniu-java-sdk</artifactId><version>7.4.0</version></dependency>

步骤二:编写工具类

// 以下拿到下载地址filePath /*** 处理异步导出消息(生成Excel文件,上传至文件服务器,并返回文件下载地址)* @param fileName 文件名* @param params 数据查询参数* @return filePath*/public static String dealWithAsyncExportMessage(String fileName, Object params) {List<UbiApiQueryLog> list = iAsyncExportService.selectExportData(params);ByteArrayOutputStream out = new ByteArrayOutputStream();try {webWriteExcel(out, list, UbiApiQueryLog.class, fileName, fileName);} catch (IOException e) {e.printStackTrace();}String filePath = QiNiuUtil.uploadFileByInputStream(fileName, new ByteArrayInputStream(out.toByteArray()));log.info("##############################################  filePath:  " + filePath);return filePath;}// 以下为生成Excel/*** 不可合并单元格** @param outputStream 输出流* @param list 数据* @param clazz JavaBean反射对象* @param fileName 文件名* @param sheetName sheetName* @throws IOException 异常*/@SuppressWarnings("rawtypes")public static void webWriteExcel(ByteArrayOutputStream outputStream, List list, Class clazz, String fileName, String sheetName) throws IOException {// 头的策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为灰headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.getIndex());// 内容的策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();HorizontalCellStyleStrategy horizontalCellStyleStrategy =new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);EasyExcelFactory.write(outputStream, clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(list);}// 以下为上传至QiNiu/*** 七牛密钥*/public static final String ACCESS_KEY = "#####";public static final String SECRET_KEY = "#####";/*** 要上传的空间名称,生成token时需要*/public static final String QI_NIU_SPACE = "#####";/*** 七牛上面文件存储域名,下载时的域名*/public static final String DOMAIN = "https://#####.com/";private static String getUpToken() {Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);String upToken = auth.uploadToken(QI_NIU_SPACE);return upToken;}/*** 通过输入流上传文件* * @param fileName 文件名* @param inputStream 输入流* @return 文件下载地址*/public static String uploadFileByInputStream(String fileName, InputStream inputStream) {//构造一个带指定 Region 对象的配置类Configuration cfg = new Configuration(Region.region0());//...其他参数参考类注释UploadManager uploadManager = new UploadManager(cfg);//默认不指定key的情况下,以文件内容的hash值作为文件名String key = fileName;String upToken = getUpToken();try {Response response = uploadManager.put(inputStream, key, upToken, null, null);if(response.isOK()){return QiNiuUtil.DOMAIN + fileName; // 必须拼接文件存储域名}//解析上传成功的结果//DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);System.out.println("上传失败!");return null;} catch (QiniuException ex) {return null;}}

步骤三:调用步骤二的工具方法dealWithAsyncExportMessage拿到最终上传至服务器后的下载地址

方式三   返回下载地址(上传至OSS文件服务器)

步骤一:导入依赖

<!-- https://mvnrepository.com/artifact/com.aliyun.oss/aliyun-sdk-oss -->
<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.13.0</version>
</dependency>

步骤二:编写OSS相关的服务类

package com.jxz.owner.sdk.oss.service;import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;/*** Created by IDEA.* User: jiangXueZhi* Date: 2021/9/27* Time: 2:56 下午*/
public interface IOssService {String uploadAndSaveName(String bucket, MultipartFile multipartFile, String saveDirAndFileName);String uploadAndSaveName(MultipartFile multipartFile, String saveDirAndFileName);String upload(String bucket, MultipartFile multipartFile);String upload(MultipartFile multipartFile);String upload(MultipartFile multipartFile, String dir);String upload(MultipartFile multipartFile, String dir, String fileName);String getDownLoadUrl(String object);String upload(InputStream in, String fileName, String dir);
}
package com.jxz.owner.sdk.oss.service.impl;import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.jxz.owner.sdk.oss.config.AliyunOssConfig;
import com.jxz.owner.sdk.oss.service.IOssService;
import com.jxz.owner.utils.StrUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;/*** Created by IDEA.* User: xiongyoulong* Date: 2021/9/27* Time: 2:58 下午*/
@Slf4j
@Service
@Component
public class OssServiceImpl implements IOssService {private OSS client = null;/*** 图片类型后缀格式**/public static final Set<String> IMG_SUFFIX = new HashSet<>(Arrays.asList("jpg", "png", "jpeg", "gif"));/*** Excel类型后缀格式**/public static final Set<String> EXCEL_SUFFIX = new HashSet<>(Arrays.asList("xls", "xlsx"));@Resourceprivate AliyunOssConfig aliyunOssConfig;/*** 初始化oss的配置信息*/@PostConstructpublic void init() {client = new OSSClientBuilder().build(aliyunOssConfig.getEndpoint(), aliyunOssConfig.getAccessKeyId(), aliyunOssConfig.getAccessKeySecret());}/*** 指定 bucket 上传** @param bucket bucket* @param multipartFile 文件* @param saveDirAndFileName  文件名* @return OSS 中的相对路径*/@Overridepublic String uploadAndSaveName(String bucket, MultipartFile multipartFile, String saveDirAndFileName) {if (bucket.trim().isEmpty()) {bucket = aliyunOssConfig.getPublicBucket();}try {// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。this.client.putObject(bucket, saveDirAndFileName, multipartFile.getInputStream());} catch (Exception exception) {log.error("upload aliyun oss file exception:{}", exception);return null;}return saveDirAndFileName;}/*** 使用默认的 Bucket 上传** @param multipartFile 文件* @param saveDirAndFileName 文件名* @return OSS 中的相对路径*/@Overridepublic String uploadAndSaveName(MultipartFile multipartFile, String saveDirAndFileName) {String bucket = aliyunOssConfig.getPublicBucket();try {// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。this.client.putObject(bucket, saveDirAndFileName, multipartFile.getInputStream());} catch (Exception exception) {log.error("upload aliyun oss file exception:{}", exception);return null;}return saveDirAndFileName;}/*** 指定 bucket 上传(文件名随机uuid)** @param bucket 默认 bucket* @param multipartFile 文件* @return OSS 中的相对路径*/@Overridepublic String upload(String bucket, MultipartFile multipartFile) {if (bucket.trim().isEmpty()) {bucket = aliyunOssConfig.getPublicBucket();}//获取文件后缀String fileSuffix = getFileSuffix(multipartFile.getOriginalFilename(), false);String saveDirAndFileName = null;try {saveDirAndFileName = "images/" + StrUtils.getUUID() + "." + fileSuffix;// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。this.client.putObject(bucket, saveDirAndFileName, multipartFile.getInputStream());} catch (Exception exception) {log.error("upload aliyun oss file exception:{}", exception);return null;}return saveDirAndFileName;}/*** 默认 bucket 上传(文件名随机uuid)** @param multipartFile 文件* @return OSS 中的相对路径*/@Overridepublic String upload(MultipartFile multipartFile) {//获取文件后缀String fileSuffix = getFileSuffix(multipartFile.getOriginalFilename(), false);String saveDirAndFileName = "";try {saveDirAndFileName = "images/" + StrUtils.getUUID() + "." + fileSuffix;// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。log.info("upload file info {}", multipartFile.getInputStream());this.client.putObject(aliyunOssConfig.getPublicBucket(), saveDirAndFileName, new ByteArrayInputStream(multipartFile.getBytes()));} catch (Exception exception) {log.error("upload aliyun oss file endpoint:{} filepath:{} exception:{}", aliyunOssConfig.getEndpoint(), saveDirAndFileName, exception);return null;}return saveDirAndFileName;}/*** 指定上传目录上传(默认 bucket 随机文件名)** @param multipartFile 文件* @param dir 目录(如:img、excel、txt等)* @return OSS 中的相对路径*/@Overridepublic String upload(MultipartFile multipartFile, String dir) {//获取文件后缀String fileSuffix = getFileSuffix(multipartFile.getOriginalFilename(), false);String saveDirAndFileName = "";try {saveDirAndFileName = dir + "/" + StrUtils.getUUID() + "." + fileSuffix;// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。log.info("upload file info {}", multipartFile.getInputStream());this.client.putObject(aliyunOssConfig.getPublicBucket(), saveDirAndFileName, new ByteArrayInputStream(multipartFile.getBytes()));} catch (Exception exception) {log.error("upload aliyun oss file endpoint:{} filepath:{} exception:{}", aliyunOssConfig.getEndpoint(), saveDirAndFileName, exception);return null;}return saveDirAndFileName;}/*** 指定上传目录、指定文件名** @param multipartFile 文件* @param dir 目录* @param fileName 文件名* @return OSS  中的相对路径*/@Overridepublic String upload(MultipartFile multipartFile, String dir, String fileName) {//获取文件后缀try {// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。log.info("upload file info {}", multipartFile.getInputStream());this.client.putObject(aliyunOssConfig.getPublicBucket(), fileName, new ByteArrayInputStream(multipartFile.getBytes()));} catch (Exception exception) {log.error("upload aliyun oss file endpoint:{} filepath:{} exception:{}", aliyunOssConfig.getEndpoint(), fileName, exception);return null;}return fileName;}/*** 上传流文件到OSS服务器** @param in 流* @param fileName 文件名* @param dir 目录* @return OSS 中的相对路径*/@Overridepublic String upload(InputStream in, String fileName, String dir) {//获取文件后缀String saveDirAndFileName = "";try {saveDirAndFileName = dir + "/" + fileName;// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。this.client.putObject(aliyunOssConfig.getPublicBucket(), saveDirAndFileName, in);} catch (Exception exception) {log.error("upload aliyun oss file endpoint:{} filepath:{} exception:{}", aliyunOssConfig.getEndpoint(), saveDirAndFileName, exception);return null;}return getDownLoadUrl(saveDirAndFileName);}/*** 获取完整的下载地址** @param dirFilePath OSS 中的相对路径* @return 完整的下载地址*/@Overridepublic String getDownLoadUrl(String dirFilePath) {OSS client1 = new OSSClientBuilder().build(aliyunOssConfig.getPublicEndpoint(), aliyunOssConfig.getAccessKeyId(), aliyunOssConfig.getAccessKeySecret());Date expiration = new Date(new Date().getTime() + 3600 * 1000);URL url = client1.generatePresignedUrl(aliyunOssConfig.getPublicBucket(), dirFilePath, expiration);return url.toString();}// 判断上传照片文件后缀是否合法public static boolean isAllowFileSuffix(String fixSuffix) {return IMG_SUFFIX.contains(fixSuffix.toLowerCase());}// 判断上传Excel是否合法public static boolean isAllowExcelFileSuffix(String fixSuffix) {return EXCEL_SUFFIX.contains(fixSuffix.toLowerCase());}/*** 获取文件的后缀名** @param appendDot 是否拼接.* @return 文件的后缀名*/public static String getFileSuffix(String fullFileName, boolean appendDot){if(fullFileName == null || !fullFileName.contains(".") || fullFileName.length() <= 1) {return "";}return (appendDot? "." : "") + fullFileName.substring(fullFileName.lastIndexOf(".") + 1);}
}

步骤三:编写OSS相关的配置类(获取 OSS 的基础配置信息)

Java 配置类:

package com.jxz.owner.sdk.oss.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** @Description: Oss 相关配置信息** @Date: 2022/1/6* @Author: jiangXueZhi*/
@Data
@Component
@ConfigurationProperties(prefix = "system.oss.aliyun-oss")
public class AliyunOssConfig {private String endpoint;private String accessKeyId;private String accessKeySecret;private String publicBucket;private String privateBucket;private String publicEndpoint;
}

yml 或者 properties 配置文件(根据自己的oss文件服务器进行配置):

#系统业务参数
system:oss:# 阿里云OSS服务配置信息aliyun-oss:endpoint: oss-cn-shanghai.aliyuncs.com  #endpoint  如: oss-cn-beijing.aliyuncs.compublic-bucket: public #公共读 桶名称private-bucket: private #私有读 桶名称access-key-id: key #AccessKeyIdaccess-key-secret: secret  #AccessKeySecretpublic-endpoint: oss-cn-shanghai.aliyuncs.com  #endpoint  如: oss-cn-beijing.aliyuncs.com

步骤四:Excel展示(多sheet)

显示效果一切正常,图中显示整体缩小是因为为了方便大家看到全局而手动缩小

多sheet页Excel展示

每页sheet存放设定的数据条数

方式四   生成Excel文件到本地

自适应表头格式类(ExcelWidthStyleStrategy) 见上述方式一的步骤一

调用方式

String localFilePath = "logs/Disburse_Timeout_Pending_" + threeHoursBeforeCurrentTimestamp + ".xlsx"; // 文件生成地址ExcelUtils.writeExcel(localFilePath, "Disburse", pendingLongList, AllBankDisbursePendingHandleEmailPojo.class); // 生成本地文件
/*** 写出一个 excel 文件到本地* <br />* 将类型所有加了 @ExcelProperty 注解的属性全部写出** @param fileName  文件路径(相对路径及绝对路径都支持)* @param sheetName sheet名* @param data      写出的数据* @param clazz     要写出数据类的Class类型对象* @param <T>       写出的数据类型*/public static <T> void writeExcel(String fileName, String sheetName, List<T> data, Class<T> clazz) {writeExcel(null, fileName, sheetName, data, clazz);}/*** 按照指定的属性名进行写出 一个 excel** @param attrName  指定的属性名 必须与数据类型的属性名一致* @param fileName  文件名 不要后缀* @param sheetName sheet名* @param data      要写出的数据* @param clazz     要写出数据类的Class类型对象* @param <T>       要写出的数据类型*/public static <T> void writeExcel(Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {fileName = StringUtils.isBlank(fileName) ? "file1" : fileName;sheetName = StringUtils.isBlank(sheetName) ? "sheet0" : sheetName;try (FileOutputStream fos = new FileOutputStream(fileName)) {write(fos, attrName, sheetName, data, clazz);LOGGER.info("Excel file generated successfully, the file location is  {}", fileName);} catch (Exception exception) {exception.printStackTrace();}}private static <T> void write(OutputStream os, Set<String> attrName, String sheetName, List<T> data, Class<T> clazz) {ExcelWriterBuilder write = EasyExcel.write(os, clazz);// 如果没有指定要写出那些属性数据,则写出全部if (!CollectionUtils.isEmpty(attrName)) {write.includeColumnFiledNames(attrName);}
//        write.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(data); // 默认表头write.registerWriteHandler(new ExcelWidthStyleStrategy()).sheet(sheetName).doWrite(data); // 自适应表头}


上传

    @RequestMapping(value = "/import", method = RequestMethod.POST)public void upload(MultipartFile file) {String cloudFile = iOssService.upload(file, "excel", file.getOriginalFilename()); // 相对路径String path = iOssService.getDownLoadUrl(cloudFile); // 绝对路径System.out.println("###########  PATH : " + path);/* 解析文件 */List<CsIssueExcel> csIssues = ExcelUtils.resolveExcel(file, CsIssueExcel.class, null);if (csIssues != null) {System.out.println(csIssues.size());}System.out.println(csIssues);}

上传基于上述  IOssService 中的接口:

String upload(MultipartFile multipartFile, String dir, String fileName);

解析基于上述 ExcelUtils 中的方法:

public static <T> List<T> resolveExcel(MultipartFile multipartFile, Class<T> clazz, ReadListener<T> readListener)

解析的注意要点

在调用解析工具方法时,接收数据的实体类一般需要另外建立,并且字段顺序与Excel表头顺序一致。

在接收数据时,接收数据的实体类中的字段写入只与Excel表头的顺序相关!!!

上传的文件:

接收解析数据的实体类

package com.jxz.owner.entity.excel;import lombok.Data;/*** @Description: CsIssue 类的上传解析接收者** @Date: 2022/1/12* @Author: jiangXueZhi*/
@Data
public class CsIssueExcel {private String issueNo;private String accountNo;private String transactionId;private String transactionDate;private Integer instituteId;private Integer operationType;private String conclusion;private String details; // 相比Excel的表头,多出的字段在解析时为null
}

解析的数据

2022-01-12 18:52:58.417  INFO 18292 --- [nio-7777-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-01-12 18:52:58.418  INFO 18292 --- [nio-7777-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2022-01-12 18:52:58.418  INFO 18292 --- [nio-7777-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
2022-01-12 18:52:58.460  INFO 18292 --- [nio-7777-exec-1] c.j.o.s.oss.service.impl.OssServiceImpl  : upload file info java.io.FileInputStream@762b8c58
###########  PATH : http://calf.oss-cn-shanghai.aliyuncs.com/issue_upload.xlsx?Expires=1641988378&OSSAccessKeyId=Rk2SDBXcOOpKzXHx&Signature=QToLbiKy7Xyhj3DlayDoswTkWFc%3D
2022-01-12 18:52:59.178  WARN 18292 --- [nio-7777-exec-1] com.jxz.owner.utils.ExcelUtils           : Parsing is complete
10
[CsIssueExcel(issueNo=ino_0, accountNo=ano_0, transactionId=tid_0, transactionDate=2022-01-12, instituteId=0, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_1, accountNo=ano_1, transactionId=tid_1, transactionDate=2022-01-12, instituteId=1, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_4, accountNo=ano_4, transactionId=tid_4, transactionDate=2022-01-12, instituteId=2, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_9, accountNo=ano_9, transactionId=tid_9, transactionDate=2022-01-12, instituteId=3, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_16, accountNo=ano_16, transactionId=tid_16, transactionDate=2022-01-12, instituteId=4, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_25, accountNo=ano_25, transactionId=tid_25, transactionDate=2022-01-12, instituteId=5, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_36, accountNo=ano_36, transactionId=tid_36, transactionDate=2022-01-12, instituteId=6, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_49, accountNo=ano_49, transactionId=tid_49, transactionDate=2022-01-12, instituteId=7, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_64, accountNo=ano_64, transactionId=tid_64, transactionDate=2022-01-12, instituteId=8, operationType=1, conclusion=这是一个测试案例, details=null), CsIssueExcel(issueNo=ino_81, accountNo=ano_81, transactionId=tid_81, transactionDate=2022-01-12, instituteId=9, operationType=1, conclusion=这是一个测试案例, details=null)]

Excel导入导出(导出有两种方式:直接返回流、返回下载地址)相关推荐

  1. 导出Word的两种方式

    导出Word有两种方式,分别是前端和后端 这两种方式在实际开发中根据实际需要会选择不同的方式 方式对比: 前端导出: 优点:简单,快捷,对于样式的设定也比较方便 缺陷:只可以导出本页展示的数据,导出的 ...

  2. 【ArcGIS Pro微课1000例】0014:两种坐标系全国1km分辨率DEM下载地址(WGS84+Albers投影)

    本文提供两种坐标系全国1km分辨率DEM下载地址(WGS84+Albers投影). 文章目录 全国1km分辨率DEM数据预览 WGS84地理坐标系 Albers投影坐标系 全国1km分辨率DEM数据下 ...

  3. EXCEL数据有效性前置校验的两种方式

    背景: 生活中经常用excel作为模板供用户批量录入数据,然后在上传系统,等到系统校验有问题后,用户又要下载进行修改重新上传,这样比较麻烦,所以如何把问题数据的发现阶段前置到用户录入的阶段,那么有问题 ...

  4. Axure导入元件库的两种方式-附完整元件库

    Axure导入原件库有两个方式: 一.通过工具导入 在Axure面板中的库点击如下所示的图标,然后选择载入元件库 二.复制元件库到软件安装目录下的DefaultSettings\Libraries下 ...

  5. Python导入模块(包)的两种方式 TypeError: 'module' object is not callable

    Python编程时明明在开始处import了相关包,但是调用函数时就报错如下: TypeError: 'module' object is not callable Python中有两种导入包(模块, ...

  6. 【IDEA】向IntelliJ IDEA创建的项目导入Jar包的两种方式

    今天用IDEA,需要导入一个Jar包,因为以前都是用eclipse的,所以对这个idea还不怎么上手,连打个Jar包都是谷歌了一下. 但是发现网上谷歌到的做法一般都是去File –> Proje ...

  7. Java 导入导出Excle表格 两种方式

    参考1:http://www.cnblogs.com/hanfeihanfei/p/7079210.html 参考2:http://blog.csdn.net/jerehedu/article/det ...

  8. 将Eclipse代码导入到AndroidStudio的两种方式

    实现步骤 1. 从Eclipse中导出Gradle build files 在Eclipse菜单中 File --> Export-->Generate Gradle build file ...

  9. 【干货:Excel中插入图片的两种方式】

    背景 业务中,客户需要导出各种数据统计报表,如果客户要求,我希望导出的报表中能插入这些数据的统计图表该如何操作,如图: 复制代码 传统POI <dependency><groupId ...

  10. 转载【IDEA】向IntelliJ IDEA创建的项目导入Jar包的两种方式

    这个博主的管用:https://blog.csdn.net/qq_26525215/article/details/53239123 第一种:File –> Project Structure导 ...

最新文章

  1. 高能预警!各路大神正火速奔赴8月WOT2016 移动互联网技术峰会
  2. 小明分享|ESP32-C3到底有哪些不一样的功能呢
  3. PAT_B_1038_Java(14分)_C++(20分)
  4. 数据结构——用栈解决回文字符问题
  5. 强化学习《基于策略 - Policy Grident》
  6. 热传导/物质扩散算法应用于推荐
  7. ireport学习笔记
  8. pythonmysqlconnector_python操作mysql——mysql.connector
  9. C++primer习题4.7比较数组是否相等
  10. 星际开图挂_《星际争霸2》牛x强力高端职业玩家手把手教你识别开图挂
  11. win10删除右键菜单多余项
  12. 【蓝桥杯备战】 Day02
  13. 【第一周:数据分析思维】7周成为数据分析师
  14. Symfony4.1 Routing 基础
  15. speedoffice表格如何快速复制整张工作表
  16. 2022年,程序员如何选择电脑
  17. IIS5、IIS6、IIS7的ASP.net 请求处理过程比较转
  18. uoj #246. 【UER #7】套路
  19. python实践 爬取豆瓣各个标签的电影 爬虫
  20. 为什么越来越多博士逃离科研?

热门文章

  1. lz4压缩算法java实现_LZ4压缩算法分析
  2. 人像动漫化 毕业设计记录
  3. Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)
  4. postgresql 修改表字段_PostgreSQL 修改表字段常用命令操作
  5. 比较两张表的数据是否相同
  6. 第二十节:本地存储 案例驱动
  7. 浏览器的混杂模式和标准模式
  8. nginx 报 accept4() failed (24: Too many open files)
  9. 数据库中INT类型概述
  10. 但凡早知道这28个网站,都不至于学得那么不扎实