数据库表导出到excel:前置知识1 ALL_TAB_COLS
数据库表导出到excel:前置知识2 Quartz基本使用
数据库表导出到excel:前置知识3 项目封装的Quartz实现动态定时任务
数据库表导出到excel:前置知识4 业务和效果
发起清单下载
control层InventoryDownloadLogController
/*
*
*/
package com.njry.sjzl.busi.rest;
import com.njry.annotation.Log;
import com.njry.sjzl.busi.domain.InventoryDownloadLog;
import com.njry.sjzl.busi.service.InventoryDownloadLogService;
import com.njry.sjzl.busi.domain.vo.InventoryDownloadLogQueryCriteria;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njry.utils.PageResult;
/**
* @author wj
* @date 2024-06-20
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "清单任务下载表管理")
@RequestMapping("/api/inventoryDownloadLog")
public class InventoryDownloadLogController {
private final InventoryDownloadLogService inventoryDownloadLogService;
@Log("导出数据")
@ApiOperation("导出数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('inventoryDownloadLog:list')")
public void exportInventoryDownloadLog(HttpServletResponse response, InventoryDownloadLogQueryCriteria criteria) throws IOException {
inventoryDownloadLogService.download(inventoryDownloadLogService.queryAll(criteria), response);
}
@Log("下载文件")
@ApiOperation("下载文件")
@GetMapping(value = "/downloadFile")
@PreAuthorize("@el.check('inventoryDownloadLog:list')")
public void exportInventoryDownloadLogFile(HttpServletResponse response, InventoryDownloadLogQueryCriteria criteria) throws IOException {
String attachmentId = criteria.getAttachmentId();
inventoryDownloadLogService.downloadFile(attachmentId, response);
}
@GetMapping
@Log("查询清单任务下载日志表")
@ApiOperation("查询清单任务下载日志表")
@PreAuthorize("@el.check('inventoryDownloadLog:list')")
public ResponseEntity<PageResult<InventoryDownloadLog>> queryInventoryDownloadLog(InventoryDownloadLogQueryCriteria criteria, Page<Object> page){
return new ResponseEntity<>(inventoryDownloadLogService.queryAll(criteria,page),HttpStatus.OK);
}
@PostMapping
@Log("新增清单任务下载日志表--发起-----------调度任务异步把table数据变成文件")
@ApiOperation("新增清单任务下载日志表--发起-----------调度任务异步把table数据变成文件")
@PreAuthorize("@el.check('inventoryDownloadLog:add')")
public ResponseEntity<Object> createInventoryDownloadLog(@Validated @RequestBody InventoryDownloadLog resources){
inventoryDownloadLogService.create(resources);
return new ResponseEntity<>(HttpStatus.CREATED);
}
// @PutMapping
// @Log("修改清单任务下载日志表")
// @ApiOperation("修改清单任务下载日志表")
// @PreAuthorize("@el.check('inventoryDownloadLog:edit')")
// public ResponseEntity<Object> updateInventoryDownloadLog(@Validated @RequestBody InventoryDownloadLog resources){
// inventoryDownloadLogService.update(resources);
// return new ResponseEntity<>(HttpStatus.NO_CONTENT);
// }
@DeleteMapping
@Log("删除清单任务下载日志表")
@ApiOperation("删除清单任务下载日志表")
@PreAuthorize("@el.check('inventoryDownloadLog:del')")
public ResponseEntity<Object> deleteInventoryDownloadLog(@RequestBody List<Long> ids) {
inventoryDownloadLogService.deleteAll(ids);
return new ResponseEntity<>(HttpStatus.OK);
}
}
service层
/*
*
*/
package com.njry.sjzl.busi.service;
import com.njry.sjzl.busi.domain.InventoryDownloadLog;
import com.njry.sjzl.busi.domain.vo.InventoryDownloadLogQueryCriteria;
import java.util.Map;
import java.util.List;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.njry.utils.PageResult;
/**
* @description 服务接口
* @author wj
* @date 2024-06-20
**/
public interface InventoryDownloadLogService extends IService<InventoryDownloadLog> {
/**
* 查询数据分页
* @param criteria 条件
* @param page 分页参数
* @return PageResult
*/
PageResult<InventoryDownloadLog> queryAll(InventoryDownloadLogQueryCriteria criteria, Page<Object> page);
/**
* 查询所有数据不分页
* @param criteria 条件参数
* @return List<InventoryDownloadLogDto>
*/
List<InventoryDownloadLog> queryAll(InventoryDownloadLogQueryCriteria criteria);
/**
* 创建
* @param resources /
*/
void create(InventoryDownloadLog resources);
// /**
// * 编辑
// * @param resources /
// */
// void update(InventoryDownloadLog resources);
/**
* 多选删除
* @param ids /
*/
void deleteAll(List<Long> ids);
/**
* 导出数据
* @param all 待导出的数据
* @param response /
* @throws IOException /
*/
void download(List<InventoryDownloadLog> all, HttpServletResponse response) throws IOException;
/**
* 导出文件
* @param attachmentId 文件名
* @param response /
* @throws IOException /
*/
void downloadFile(String attachmentId, HttpServletResponse response) throws IOException;
}
实现层impl(主要看create方法)
/*
*
*/
package com.njry.sjzl.busi.service.impl;
import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.njry.config.FileProperties;
import com.njry.sjzl.busi.attachment.domain.Attachment;
import com.njry.sjzl.busi.attachment.domain.vo.AttachmentQueryCriteria;
import com.njry.sjzl.busi.attachment.mapper.AttachmentMapper;
import com.njry.sjzl.busi.domain.Inventory;
import com.njry.sjzl.busi.domain.InventoryAuth;
import com.njry.sjzl.busi.domain.InventoryDownloadLog;
import com.njry.sjzl.busi.domain.vo.InventoryQueryCriteria;
import com.njry.sjzl.busi.mapper.AtomBusiCategoryMapper;
import com.njry.sjzl.busi.mapper.InventoryAuthMapper;
import com.njry.sjzl.busi.mapper.InventoryMapper;
import com.njry.sjzl.busi.task.GenerateExcelTask;
import com.njry.sjzl.busi.task.vo.TransferVo;
import com.njry.utils.*;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njry.sjzl.busi.service.InventoryDownloadLogService;
import com.njry.sjzl.busi.domain.vo.InventoryDownloadLogQueryCriteria;
import com.njry.sjzl.busi.mapper.InventoryDownloadLogMapper;
import org.apache.poi.util.IOUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
/**
* @description 服务实现
* @author wj
* @date 2024-06-20
**/
@Service
@RequiredArgsConstructor
public class InventoryDownloadLogServiceImpl extends ServiceImpl<InventoryDownloadLogMapper, InventoryDownloadLog> implements InventoryDownloadLogService {
private final InventoryDownloadLogMapper inventoryDownloadLogMapper;
private final InventoryMapper inventoryMapper;
private final AttachmentMapper attachmentMapper;
private final FileProperties properties;
// 递归查询所属资产
private final AtomBusiCategoryMapper atomBusiCategoryMapper;
@Override
public PageResult<InventoryDownloadLog> queryAll(InventoryDownloadLogQueryCriteria criteria, Page<Object> page){
IPage<InventoryDownloadLog> all = inventoryDownloadLogMapper.findAll(criteria, page);
List<InventoryDownloadLog> records = all.getRecords();
// 根据每条数据类的busiSort递归向下查找归属业务分类(回显)
if(records.size() > 0 ){
for (int i = 0; i < records.size(); i++) {
Set<String> taskSetResult = new LinkedHashSet<>();
Long categoryId = records.get(i).getCategoryId();
if(categoryId != null){
List<String> subCategory = atomBusiCategoryMapper.findSubCategory(categoryId);
String currentCategoryName = atomBusiCategoryMapper.findCategoryNameByCateforyId(categoryId);
taskSetResult.addAll(subCategory);
taskSetResult.add(currentCategoryName);
String temp = "";
for(String item : taskSetResult){
temp += ","+item;
}
String result = temp.substring(1);
records.get(i).setCategoryName(result);
}
}
}
return PageUtil.toPage(all);
}
@Override
public List<InventoryDownloadLog> queryAll(InventoryDownloadLogQueryCriteria criteria){
return inventoryDownloadLogMapper.findAll(criteria);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void create(InventoryDownloadLog resources) {
// 前端发起只传inventoryId,别的基本数据这里查完赋值
Long inventoryId = resources.getInventoryId();
Inventory inventory = inventoryMapper.queryDetailByInventoryId(inventoryId);
resources.setInventoryName(inventory.getInventoryName());
resources.setCategoryId(inventory.getCategoryId());
// 新增的时候把操作人和操作时间
Timestamp timestamp = DateTime.now().toTimestamp();
// String username = "System";
// try {username = SecurityUtils.getCurrentUsername();}catch (Exception ignored){}
// 操作人
resources.setOperId(SecurityUtils.getCurrentUserId());
resources.setOperDate(timestamp);
// 状态:0 待生成 2 正在生成 1 数据生成完成
resources.setStatus(2);
// 正在生成没有附件id
resources.setAttachmentId("");
Long seq = inventoryDownloadLogMapper.getSeq();
resources.setId(seq);
save(resources);
// 异步生成文件(查询数据转成excel文件)
Long currentUserId = SecurityUtils.getCurrentUserId();
// 获取系统定义的文件路径
String dirPath = properties.getPath().getInventory();//系统文件路径下inventory
String separator = properties.getPath().getSeparator();//获取系统分隔符
// /inventory/日期/
Date currentDate = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM");
String currentYearMonth = formatter.format(currentDate);
System.out.println(currentYearMonth);
dirPath += currentYearMonth + separator;
// 生成需要的参数加入队列
TransferVo transferVo = new TransferVo();
transferVo.setDirPath(dirPath);
transferVo.setFileSuffix("xlsx");
transferVo.setSeq(seq);
transferVo.setInventoryId(inventoryId);
transferVo.setSeparator(separator);
// transferVo.setTableHead(tableHead);//费时间,放在异步里面
// transferVo.setMaps(maps);//费时间,放在异步里面
transferVo.setCurrentUserId(currentUserId);
GenerateExcelTask.addDataList(transferVo);
}
// @Override
// @Transactional(rollbackFor = Exception.class)
// public void update(InventoryDownloadLog resources) {
// InventoryDownloadLog inventoryDownloadLog = getById(resources.getId());
// inventoryDownloadLog.copy(resources);
// saveOrUpdate(inventoryDownloadLog);
// }
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteAll(List<Long> ids) {
removeBatchByIds(ids);
}
@Override
public void download(List<InventoryDownloadLog> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (InventoryDownloadLog inventoryDownloadLog : all) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("唯一标识 序列", inventoryDownloadLog.getLogId());
map.put("清单ID", inventoryDownloadLog.getInventoryId());
map.put("清单名称", inventoryDownloadLog.getInventoryName());
map.put("归属业务类别 T_atom_busi_category.CATEGORY_ID", inventoryDownloadLog.getCategoryId());
map.put("操作人", inventoryDownloadLog.getOperId());
map.put("操作时间", inventoryDownloadLog.getOperDate());
map.put("状态:0 发起任务 2 正在生成 1 数据生成完成", inventoryDownloadLog.getStatus());
map.put("附件编号", inventoryDownloadLog.getAttachmentId());
map.put("提醒手机号码", inventoryDownloadLog.getMobile());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
// 将文件转成响应流给前端
@Override
public void downloadFile(String attachmentId, HttpServletResponse response) throws IOException {
// 根据文件id查找文件所在位置 文件名
String fileName = "";
String dirPath = "";
String separator = properties.getPath().getSeparator();//获取系统分隔符
AttachmentQueryCriteria attachmentQueryCriteria = new AttachmentQueryCriteria();
attachmentQueryCriteria.setAttachmentId(attachmentId);
Attachment attachment = attachmentMapper.queryAllWithCondition(attachmentQueryCriteria);
if(attachment != null){
dirPath = attachment.getAttachmentPath();
fileName = attachment.getFileName();
}
// 因为保存的时候是在默认路径下加一个watermarkPath文件夹下生成同名的文件,这里取得时候也得取水印的文件
String watermarkPath = dirPath + "watermarkPath" + separator;
FileInputStream watermarkFileInput = null;
//response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
ServletOutputStream out = response.getOutputStream();
File watermarkFile = new File(watermarkPath, fileName);
watermarkFileInput = new FileInputStream(watermarkFile);
IOUtils.copy(watermarkFileInput,out);
watermarkFile.deleteOnExit();
watermarkFileInput.close();
}
}
生成的文件位置
/*
*
*/
package com.njry.config;
import lombok.Data;
import com.njry.utils.ElConstant;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author njry
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "file")
public class FileProperties {
/** 文件大小限制 */
private Long maxSize;
/** 头像大小限制 */
private Long avatarMaxSize;
private ElPath mac;
private ElPath linux;
private ElPath windows;
public ElPath getPath(){
// System.getProperty() 方法用于获取系统属性的值
String os = System.getProperty("os.name");
if(os.toLowerCase().startsWith(ElConstant.WIN)) {
return windows;
} else if(os.toLowerCase().startsWith(ElConstant.MAC)){
return mac;
}
return linux;
}
@Data
public static class ElPath{
private String path;
private String avatar;
private String inventory;
private String separator;
}
}
配置文件里写
# 文件存储路径
file:
mac:
path: ~/file/
avatar: ~/avatar/
inventory: /file/inventory/
separator: /
linux:
path: /home/eladmin/file/
avatar: /home/eladmin/avatar/
inventory: /eladmin/file/inventory/
separator: /
windows:
path: D:\sjzl\file\
avatar: D:\sjzl\avatar\
# inventory: D:\sjzl\file\inventory\ ## 不设置固定路径,相对路径自动找到项目所在盘位置
inventory: \sjzl\file\inventory\
separator: \
# 文件大小 /M
maxSize: 100
avatarMaxSize: 5
mapper层
/*
*
*/
package com.njry.sjzl.busi.mapper;
import com.njry.sjzl.busi.domain.InventoryDownloadLog;
import com.njry.sjzl.busi.domain.vo.InventoryDownloadLogQueryCriteria;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
* @author wj
* @date 2024-06-20
**/
@Mapper
public interface InventoryDownloadLogMapper extends BaseMapper<InventoryDownloadLog> {
IPage<InventoryDownloadLog> findAll(@Param("criteria") InventoryDownloadLogQueryCriteria criteria, Page<Object> page);
List<InventoryDownloadLog> findAll(@Param("criteria") InventoryDownloadLogQueryCriteria criteria);
List<String> findTableHead(@Param("tableName") String tableName,@Param("owner") String owner);
List<Map<String, Object>> commonSql(@Param("selectColumnSql") String selectColumnSql,@Param("tableName") String tableName,@Param("whereSql") String whereSql);
Long getSeq();
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.njry.sjzl.busi.mapper.InventoryDownloadLogMapper">
<resultMap id="BaseResultMap" type="com.njry.sjzl.busi.domain.InventoryDownloadLog">
<result column="LOG_ID" property="logId"/>
<result column="LOG_ID" property="id"/>
<result column="INVENTORY_ID" property="inventoryId"/>
<result column="INVENTORY_NAME" property="inventoryName"/>
<result column="CATEGORY_ID" property="categoryId"/>
<result column="OPER_ID" property="operId"/>
<result column="OPER_NAME" property="operName"/>
<result column="OPER_DATE" property="operDate"/>
<result column="STATUS" property="status"/>
<result column="ATTACHMENT_ID" property="attachmentId"/>
<result column="MOBILE" property="mobile"/>
</resultMap>
<sql id="Base_Column_List">
LOG_ID, INVENTORY_ID, INVENTORY_NAME, CATEGORY_ID, OPER_ID, OPER_DATE, STATUS, ATTACHMENT_ID, MOBILE
</sql>
<sql id="Prefix_Base_Column_List">
tidl.LOG_ID, tidl.INVENTORY_ID, tidl.INVENTORY_NAME, tidl.CATEGORY_ID, tidl.OPER_ID, tidl.OPER_DATE, tidl.STATUS, tidl.ATTACHMENT_ID, tidl.MOBILE
</sql>
<select id="findAll" resultMap="BaseResultMap">
select tuser.name OPER_NAME,
<include refid="Prefix_Base_Column_List"/>
from T_INVENTORY_DOWNLOAD_LOG tidl
left join T_USER tuser on tuser.user_id = tidl.OPER_ID
<where>
<if test="criteria.inventoryId != null">
and tidl.INVENTORY_ID = #{criteria.inventoryId}
</if>
<if test="criteria.inventoryName != null">
and tidl.INVENTORY_NAME like concat('%'||#{criteria.inventoryName},'%')
</if>
<if test="criteria.status != null">
and tidl.STATUS = #{criteria.status}
</if>
<if test="criteria.mobile != null">
and tidl.MOBILE = #{criteria.mobile}
</if>
</where>
</select>
<select id="findTableHead" resultType="java.lang.String">
select a.column_name from all_tab_cols a
where a.table_name=upper(#{tableName}) and owner=#{owner} order by a.column_id
</select>
<select id="commonSql" resultType="java.util.Map">
select ${selectColumnSql} from ${tableName}
<if test="whereSql != ''">
where ${whereSql}
</if>
</select>
<select id="getSeq" resultType="java.lang.Long">
select seq_T_INVENTORY_DOWNLOAD_LOG.nextval user_user_id from dual
</select>
</mapper>
自己定义了一个任务调度然后可以启动调度
package com.njry.sjzl.busi.task;
import cn.hutool.core.date.DateTime;
import com.njry.sjzl.busi.attachment.domain.Attachment;
import com.njry.sjzl.busi.attachment.mapper.AttachmentMapper;
import com.njry.sjzl.busi.domain.Inventory;
import com.njry.sjzl.busi.domain.InventoryAuth;
import com.njry.sjzl.busi.domain.InventoryDownloadLog;
import com.njry.sjzl.busi.mapper.InventoryAuthMapper;
import com.njry.sjzl.busi.mapper.InventoryDownloadLogMapper;
import com.njry.sjzl.busi.mapper.InventoryMapper;
import com.njry.sjzl.busi.task.vo.TransferVo;
import com.njry.utils.GenerateExcelToFile;
import com.njry.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Map;
import java.util.Queue;
@Service
@RequiredArgsConstructor
public class GenerateExcelTask {
/**
* 定义一个队列
*/
// 定义一个队列
static Queue<TransferVo> queue = new ArrayDeque<>();
private final AttachmentMapper attachmentMapper;
private final InventoryDownloadLogMapper inventoryDownloadLogMapper;
private final InventoryAuthMapper inventoryAuthMapper;
private final InventoryMapper inventoryMapper;
public static void addDataList(TransferVo transferVo){
queue.offer(transferVo);
}
public static TransferVo pollDataList(){
TransferVo poll = queue.poll();// poll 方法用于获取队列头部的元素并移除它
return poll;
}
public void run(){
System.out.println("在sjzl-busi下面的bean被任务调度");
// 异步生成excel
try {
TransferVo transferVo = GenerateExcelTask.pollDataList();
System.out.println("取出一个数据后队列长度:"+queue.size());
if(transferVo != null){
String dirPath = transferVo.getDirPath();
// List<Map<String, Object>> maps = transferVo.getMaps();
// List<String> tableHead = transferVo.getTableHead();
String fileSuffix = transferVo.getFileSuffix();
Long seq = transferVo.getSeq();
Long currentUserId = transferVo.getCurrentUserId();
Long inventoryId = transferVo.getInventoryId();
String separator = transferVo.getSeparator();
// 根据清单inventoryId和当前用户判断拥有的权限(要不要放where条件) 清单用户权限T_INVENTORY_AUTH
// AUTH_TYPE 权限类别:1 全量 不放where 2 单元 (清单选择的列名和权限里的单元编号作为where条件)
// AREA_ID 单元编号
InventoryAuth inventoryAuth = inventoryAuthMapper.findByUserIdAndInventoryId(currentUserId, inventoryId);
Inventory inventory = inventoryMapper.queryDetailByInventoryId(inventoryId);
Integer authType = inventoryAuth.getAuthType();
Long areaId = inventoryAuth.getAreaId();
String whereSql = "";
if(authType == 2){
String queryColName = inventory.getQueryColName();
whereSql = queryColName + "=" + areaId;
}
String tableName = inventory.getTableName();
// tableName = "t_organization";//写死测试
// tableName = "atom_base_info";//写死测试分sheet
// whereSql = "";//写死测试(配置的列名称不对)
String owner = "NJDATA";//从配置里读取
// 获取表的列名-------作为select xxx,xxx,xxx,xxx from 表名
// select a.column_name from all_tab_cols a
// where a.table_name=upper('t_organization') and owner='NJDATA' order by a.column_id;
List<String> tableHead = inventoryDownloadLogMapper.findTableHead(tableName, owner);
String selectColumnSql = String.join(",",tableHead);
// 通用查询sql,传入查询字段,表名和where条件(区分#{}和${}的时候了)
List<Map<String, Object>> maps = inventoryDownloadLogMapper.commonSql(selectColumnSql, tableName, whereSql);
// 将数据生成excel文件里面
Map<String, Object> resultMap = GenerateExcelToFile.dataToExcel(maps, tableHead, dirPath, fileSuffix,separator);
Attachment attachment = new Attachment();
// 将文件数据保存文件附件表 T_ATTACHMENT
attachment.setAttachmentId(resultMap.get("attachmentId").toString());
attachment.setFileName(resultMap.get("fileName").toString());
attachment.setFileType(resultMap.get("fileType").toString());
attachment.setFileExtension(resultMap.get("fileExtension").toString());
attachment.setFileSize(resultMap.get("fileSize").toString());
attachment.setAttachmentPath(resultMap.get("attachmentPath").toString());
// 新增的时候把操作人和操作时间
Timestamp timestampAgain = DateTime.now().toTimestamp();
attachment.setOperDate(timestampAgain);
// 操作人
// SecurityContext context = SecurityContextHolder.getContext();
// System.out.println("SecurityContext:这里可以获取到吗"+context);
// attachment.setOperId(SecurityUtils.getCurrentUserId());
attachment.setOperId(currentUserId);
attachmentMapper.insert(attachment);
// 在文件生成记录T_INVENTORY_DOWNLOAD_LOG 修改文件生成状态 和添加附件编号
InventoryDownloadLog inventoryDownloadLog = new InventoryDownloadLog();
inventoryDownloadLog.setId(seq);
inventoryDownloadLog.setAttachmentId(resultMap.get("attachmentId").toString());
inventoryDownloadLog.setStatus(1);
inventoryDownloadLogMapper.updateById(inventoryDownloadLog);
// 发送短信通知用户生成结束
}else{
// 这里应该降低任务执行调度的频率
System.out.println("暂时没有可以执行的生成文件");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
工具类(将数据库数据转成file保存)
package com.njry.utils;
import cn.hutool.core.util.IdUtil;
import com.njry.config.FileProperties;
import com.njry.exception.BadRequestException;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GenerateExcelToFile {
/**
*
* @param list 数据库查询出来的信息
* @param tableHead 数据库的表头(简单表头)
* @param dirPath 保存的文件路径
* @param fileSuffix 生成的excel格式后缀
* @param separator 生成的excel带水印格要多加一层文件路径
* @throws IOException
*/
public static Map<String, Object> dataToExcel(List<Map<String, Object>> list,List<String> tableHead,String dirPath, String fileSuffix, String separator) throws IOException {
Map<String, Object> resultMap = new HashMap<>();
Workbook wb = null;
FileInputStream tempInput = null;
String watermarkPath = dirPath + "watermarkPath" + separator;
int type = 3;//默认用type区分一个sheet有多少行
if("xlsx".equals(fileSuffix)){
wb = new XSSFWorkbook();
type = 1;
}
if("xls".equals(fileSuffix)){
wb = new HSSFWorkbook();
type = 2;
}
Workbook exportWorkbook = export(wb, list, tableHead, type);
String attachmentId = IdUtil.simpleUUID();
String fileSize = "";
String to_file_name = attachmentId + "." + fileSuffix; // 结果文件名称
// 判断保存的文件路径是否存在,不存在就创建
File outFileExist = new File(dirPath);
if(!outFileExist.exists()){
outFileExist.mkdirs();
}
File outFile = new File(dirPath, to_file_name);
try {
FileOutputStream outStream = new FileOutputStream(outFile);
// 写入Workbook到文件
exportWorkbook.write(outStream);
// 也可以通过流获取大小
// long size = outStream.getChannel().size();
// 强制刷新文件流,确保所有数据都被写入到文件中
outStream.flush();
// 获取文件对象
File outputFile = new File(dirPath, to_file_name);
long length = outputFile.length();
fileSize = length + " bytes";
outStream.close();
} catch (Exception e) {
throw new BadRequestException("导出结果文件异常:" + e);
}
// 将有数据的excel的文件再加水印
File tempFile = new File(dirPath, to_file_name);
tempInput = new FileInputStream(tempFile);
XSSFWorkbook xSSFWorkbook = new XSSFWorkbook(tempInput);
// 将导出的数据加水印放到另一个文件watermarkPath里面
File watermarkFileExist = new File(watermarkPath, to_file_name);
PoiSecurity.addWatermarkToXlsx(new String[]{"test"},xSSFWorkbook,watermarkFileExist);
xSSFWorkbook.close();
tempInput.close();
// 终止后删除临时文件
tempFile.deleteOnExit();
// 处理文件信息返回
resultMap.put("attachmentId",attachmentId);
resultMap.put("fileName",to_file_name);
resultMap.put("fileType","excel");
resultMap.put("fileExtension",fileSuffix);
resultMap.put("fileSize",fileSize);
resultMap.put("attachmentPath",dirPath);
return resultMap;
}
/**
*
* @param wb 操作的Workbook
* @param list 数据库查询出来的信息
* @param tableHead 数据库的表头(简单表头)
* @param type excel类型 xlsx 1 xls 2 -------区分sheet最大告诉
* @return
*/
public static Workbook export(Workbook wb,List<Map<String, Object>> list, List<String> tableHead,int type) {
HSSFWorkbook HSSwb = null;
XSSFWorkbook XSSwb = null;
// 不定义sheet名字,自生成
// Excel 2003及更早的版本中,行数上限是65,536行
// 2007开始,行数上限增加到了1,048,576行
int maxRow = 49999;//去除一个表头行
if(type == 1){
maxRow = 1048575;//去除一个表头行
XSSwb = (XSSFWorkbook)wb;
}
if(type == 2){
maxRow = 65535;//去除一个表头行
HSSwb = (HSSFWorkbook)wb;
}
maxRow = 49999;//去除一个表头行(无论啥格式默认都是50000一个sheet)
// 处理数据需要多少个sheet
int size = list.size();
int result = size / maxRow + 1;
if(result == 0){
result = 1;
}
// 循环sheet
for (int i = 0; i < result; i++) {
if(type == 1){
XSSFSheet sheet = XSSwb.createSheet();
// 处理每个sheet的表头
XSSFRow row = sheet.createRow((short) 0);
Cell cell = null;
for (int j = 0; j < tableHead.size(); j++) {
cell = row.createCell(j);
// cell.setCellStyle(headStyle);
cell.setCellValue(tableHead.get(j));
}
// 写入数据
for (int n = 0 + maxRow * i; n < maxRow * (i + 1); n++) {
// 判断数据list的大小是否大于要创建的行
if(size - 1 >= n ){//下面list.get(n)就取不到数据,不应该继续创建行 size 14 get(n)时候 n只能到13
row = sheet.createRow(n % maxRow + 1);
Cell dataCell = null;
for (int m = 0; m < tableHead.size(); m++) {
dataCell = row.createCell(m);
dataCell.setCellValue(StringUtils.notEmpty(list.get(n).get(tableHead.get(m))));
}
}
}
}
if(type == 2){
HSSFSheet sheet = HSSwb.createSheet();
// 处理每个sheet的表头
HSSFRow row = sheet.createRow((short) 0);
Cell cell = null;
for (int j = 0; j < tableHead.size(); j++) {
cell = row.createCell(j);
// cell.setCellStyle(headStyle);
cell.setCellValue(tableHead.get(j));
}
// 写入数据
for (int n = 0 + maxRow * i; n < maxRow * (i + 1); n++) {
// 判断数据list的大小是否大于要创建的行
if(size - 1 >= n ){//下面list.get(n)就取不到数据,不应该继续创建行 size 14 get(n)时候 n只能到13
row = sheet.createRow(n % maxRow + 1);
Cell dataCell = null;
for (int m = 0; m < tableHead.size(); m++) {
dataCell = row.createCell(m);
dataCell.setCellValue(StringUtils.notEmpty(list.get(n).get(tableHead.get(m))));
}
}
}
}
}
return wb;
}
}
工具类PoiSecurity(之前贴过)
package com.njry.utils;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.GeneralSecurityException;
import javax.imageio.ImageIO;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionInfoBuilder;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.crypt.binaryrc4.BinaryRC4EncryptionInfoBuilder;
import org.apache.poi.poifs.crypt.binaryrc4.BinaryRC4Encryptor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.core.io.ClassPathResource;
public class PoiSecurity {
public static void addWatermarkToXlsx(String[] content,XSSFWorkbook xssfworkbook,File outfile) throws IOException{
ByteArrayOutputStream bytestream = createWaterMark(content,300,250);
// XSSFWorkbook下面有几个sheet
int sheetsize = xssfworkbook.getNumberOfSheets();
for(int index=0;index<sheetsize;index++){
XSSFSheet sheet = xssfworkbook.getSheetAt(index);
XSSFWorkbook workbook = sheet.getWorkbook();
// bytestream.toByteArray()(前置知识)
// 下面这一串看不懂(没看官网文档的下场)
int pictureIdx = workbook.addPicture(bytestream.toByteArray(), Workbook.PICTURE_TYPE_PNG);
POIXMLDocumentPart poixmlDocumentPart = workbook.getAllPictures().get(pictureIdx);
PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();
String relType = XSSFRelation.IMAGES.getRelation();
PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
}
if(!outfile.exists()){
File par = outfile.getParentFile();
if(!par.exists()){
par.mkdirs();
}
outfile.createNewFile();
}
FileOutputStream out = new FileOutputStream(outfile);
xssfworkbook.write(out);
out.close();
}
public static void addImageToXlsx(String[] content,XSSFWorkbook xssfworkbook,File outfile) throws IOException{
int imgW = 300,imgH = 250;
ByteArrayOutputStream bytestream = createWaterMark(content,imgW,imgH);
byte[] bytes = bytestream.toByteArray();
int sheetsize = xssfworkbook.getNumberOfSheets();
for(int index=0;index<sheetsize;index++){
XSSFSheet sheet = xssfworkbook.getSheetAt(index);
int[] wh = getSheetWidthAndHeight(sheet);
int width = wh[0];
int height = wh[1];
XSSFDrawing drawingPatriarch = sheet.createDrawingPatriarch();
//根据宽高进行插入
for(int i = 0;i< (width / imgW) + 1;i++) { //x轴
for(int j = 0; j <( height / imgH) + 1;j++){ //y轴
int x = i * imgW;
int y = j * imgH;
//根据图片插入
/*XSSFClientAnchor anchor = new XSSFClientAnchor(x,y,x+width,y+height,0,0,1,1);
int pIndex = xssfworkbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
drawingPatriarch.createPicture(anchor,pIndex);*/
}
}
}
if(!outfile.exists()){
File par = outfile.getParentFile();
if(!par.exists()){
par.mkdirs();
}
outfile.createNewFile();
}
FileOutputStream out = new FileOutputStream(outfile);
xssfworkbook.write(out);
out.close();
}
/**
* 获取sheet的宽高
* @param sheet
* @return
*/
private static int[] getSheetWidthAndHeight(Sheet sheet){
float height = 0f;
float width = 0f;
int firstRowNum = sheet.getFirstRowNum();
int lastRowNum = sheet.getLastRowNum();
for (int i = firstRowNum; i < lastRowNum; i++) {
Row row = sheet.getRow(i);
if(i == firstRowNum){ //获取宽度
short firstCellNum = row.getFirstCellNum();
short lastCellNum = row.getLastCellNum();
for(int j = firstCellNum;j<lastCellNum;j++){
width = width + sheet.getColumnWidthInPixels(j);
}
}
height = height+ row.getHeightInPoints();
}
return new int[]{Math.round(width),Math.round(height)};
}
private static ByteArrayOutputStream createWaterMark(String[] content,int width,int height) throws IOException {
ClassPathResource resource = new ClassPathResource("font/simsun.ttc");//宋体
InputStream resourceAsStream = null;
Font font = null;
try{
resourceAsStream = resource.getInputStream();
font = Font.createFont(Font.TRUETYPE_FONT, resourceAsStream);
font = font.deriveFont(20f);
}catch (Exception e){
throw new IOException("PoiSecurity createWaterMark error",e);
} finally {
if(resourceAsStream!=null){
resourceAsStream.close();
}
}
// (前置知识)
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);// 获取bufferedImage对象
// 设置字体(前置知识)
Graphics2D g2d = image.createGraphics(); // 获取Graphics2d对象(前置知识)
// 这里实在看不懂,为啥销毁后用BufferedImage重新获取Graphics2d对象
image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = image.createGraphics();
g2d.setColor(new java.awt.Color(0, 0, 0, 40));//设置字体颜色和透明度,最后一个参数为透明度(前置知识)
g2d.setStroke(new BasicStroke(1)); // 设置字体
g2d.setFont(font); // 设置字体类型 加粗 大小
g2d.rotate(-0.5, (double) image.getWidth() / 2, (double) image.getHeight() / 2);//设置倾斜度
// 要想得到表示屏幕设备字体属性的对象(前置知识)
FontRenderContext context = g2d.getFontRenderContext();
// 循环设置水印文字位置(看不懂)
for (int i = 0; i < content.length; i++) {
// 返回包围字符串的矩形(前置知识)
if(content[i] == null){
continue;
}
Rectangle2D bounds = font.getStringBounds(content[i], context);
// 宽度
double x = (width - bounds.getWidth()) / 2;
// 高度
double y = (height - bounds.getHeight()) / 2;
// 上坡度
double ascent = -bounds.getY();
double baseY = y + ascent;
// 写入水印文字原定高度过小,所以累计写水印,增加高度
g2d.drawString(content[i], (int) x, (int) baseY+(30*i));
}
// 设置透明度(前置知识)
// g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER));
// 释放对象
g2d.dispose();
// ByteArrayOutputStream(前置知识)
ByteArrayOutputStream os = new ByteArrayOutputStream();
// BufferedImage –> byte[](前置知识)
ImageIO.write(image, "png", os);
return os;
}
public static void encXlsx(String paasword,File file) throws InvalidFormatException, IOException, GeneralSecurityException{
try (POIFSFileSystem fs = new POIFSFileSystem()) {
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
// EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null);
Encryptor enc = info.getEncryptor();
enc.confirmPassword(paasword);
// Read in an existing OOXML file and write to encrypted output stream
// don't forget to close the output stream otherwise the padding bytes aren't added
try (OPCPackage opc = OPCPackage.open(file, PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs)) {
opc.save(os);
}
// Write out the encrypted version
try (FileOutputStream fos = new FileOutputStream(file)) {
fs.writeFilesystem(fos);
}
}
}
}