数据库表导出到excel

数据库表导出到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);
		    }
		}
	}


}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

#### golang中【堆】的使用及底层 ####

声明&#xff0c;本文部分内容摘自&#xff1a; Go: 深入理解堆实现及应用-腾讯云开发者社区-腾讯云 数组实现堆 | WXue 堆&#xff08;Heap&#xff09;是实现优先队列的数据结构&#xff0c;Go提供了接口和方法来操作堆。 应用 package mainimport ("container/heap&q…

LVS-DR群集

LVS-DR集群 LVS-DR(Linux Virtual Server DIrector Server)工作模式&#xff0c;是生产环境中最常用的一种工作模式。 LVS-DR工作原理 LVS-DR模式&#xff0c;Director Server作为群集的访问入口&#xff0c;不作为网关使用&#xff0c;节点DirectorServer与Real Server需要…

光速入门 Tailwind CSS

文章目录 入门安装IDE 设置使用预编译器生产环境优化 基础概念分层指令tailwindlayerapplyconfig 函数theme()screen() 基础案例怎么设置属性任意值&#xff1f;hover 父元素时&#xff0c;怎么选中子元素添加样式&#xff1f;添加 animation 动画 配置主题 Tailwind CSS 中文网…

性能测试-JMeter学习

1、给不同的访问口分配访问占比&#xff1b;例&#xff1a;登录30%&#xff0c;首页&#xff1a;20%&#xff0c;新增&#xff1a;50% 不同业务放到不同线程组里&#xff0c;实现不同业务的分配 使用吞吐量控制器&#xff0c;设置不同的占比 使用if控制器&#xff0c;设置不同…

HX4004A-MFC 低噪声、稳压电荷泵DC/DC转换器芯片IC

一般描述 该HX4004A是一个低噪声开关电容电压倍。它产生一个调节输出电压从2.7V到4.5V的输入。低的外部零件数量(VIN和VOUT处一个飞行电容和两个小型旁路电容)使HX4004A非常适合小型电池供电应用。 该HX4004A具有热关断能力&#xff0c;可以生存从VOUT到GND的连续…

【pytorch13】激活函数及梯度

什么是激活函数 计算机科学家借鉴生物的神经元机制发明了计算机上的模型&#xff0c;这个模型与生物的神经元非常类似 激活的意思就是z变量要大于0&#xff0c;这一个节点才会激活&#xff0c;否则就会处于睡眠状态不会输出电平值 该激活函数在z0处不可导&#xff0c;因此不能…

地级市空气质量指数AQI、环境污染PM2.5、SO2

2015-2021年地级市月度空气质量数据&#xff08;AQI、SO2、NO2、PM2.5、PM10、O3、CO&#xff09; 目录 探究环境污染对经济增长的影响 一、引言 二、数据来源与描述性统计 三、实证模型 &#xff08;一&#xff09;模型设定 &#xff08;二&#xff09;变量说明 四、程…

混元大模型加持,微信输入法开启AI问答新体验

在人工智能技术飞速发展的今天&#xff0c;微信作为全球最大的社交平台之一&#xff0c;一直在不断地探索和创新&#xff0c;以提供更智能、更便捷的用户体验。 最近&#xff0c;微信官方宣布了一个令人兴奋的消息&#xff1a;微信输入法正式上线了“一键AI问答”功能&#xf…

【Python机器学习】算法链与管道——通用的管道接口

Pipeline类补单可以用于预处理和分类&#xff0c;实际上还可以将任意数量的估计器连接在一起。例如&#xff0c;我们可以构建一个包含特征提取、特征选择、缩放和分类的管道&#xff0c;总共有4个步骤。同样的&#xff0c;最后一步可以用聚类或回归代替。 对于管道中估计器的唯…

【机器学习】Datawhale-AI夏令营分子性质AI预测挑战赛

参赛链接&#xff1a;零基础入门 Ai 数据挖掘竞赛-速通 Baseline - 飞桨AI Studio星河社区 一、赛事背景 在当今科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的深度和广度渗透到科研领域&#xff0c;特别是在化学及药物研发中展现出了巨…

警翼警用记录仪视频格式化后恢复方法

警翼是国内较大的一家警用记录仪厂商&#xff0c;此品牌我们恢复过很多&#xff0c;此次遇到的是一个典型的误格式化的情况&#xff0c;我们来看看误格式化后如何恢复。 故障存储: 32G卡/fat32 故障现象: 客户提供的信息是在交接设备后没有及时备份而做出了初始化设备的操…

fluwx插件实现微信支付

Flutter开发使用fluwx插件实现微信支付&#xff0c;代码量不多&#xff0c;复杂的是安卓和iOS的各种配置。 在 pubspec.yaml 文件中添加fluwx依赖 fluwx: ^4.5.5 使用方法 通过fluwx注册微信Api await Fluwx().registerApi(appId: wxea7a1c53d9e5849d, universalLink: htt…

机器人控制系列教程之Delta机器人动力学分析

动力学简介 机器人动力学分析是已知各运动构件的尺寸参数和惯性参数的情况下,求解末端运动状态与主驱动力矩之间的函数关系。 意义:对并联机器人动力学分析的意义体现在: 为伺服电机的选型提供理论依据;获得动力学参数为目标函数的最优问题做性能评价指标;为高精度控制提…

内容为王:揭秘顶尖品牌的内容营销制胜法宝

内容营销是当今互联网市场推广领域的热门话题&#xff0c;因为它可以帮助企业更好地与受众沟通、建立品牌口碑&#xff0c;增加销售量。 根据咱们何策网的资源库里的SocialBeta2024年最新《2024 内容营销 10 大趋势》的报告来看&#xff0c;品牌在未来内容营销中最应该注重的是…

2024亚太杯中文赛数学建模B题【洪水灾害的数据分析与预测】思路详解

2024 年第十四届 APMCM 亚太地区大学生数学建模竞赛 B题 洪水灾害的数据分析与预测 附件 train.csv 中提供了超过 100 万的洪水数据&#xff0c;其中包含洪水事件的 id、季风强度、地形排水、河流管理、森林砍伐、城市化、气候变化、大坝质量、淤积、农业实践、侵蚀、无效防灾、…

Unity 之基于URP使用UniStorm Weather System天气系统

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity 之基于URP使用UniStorm Weather System天气系统 TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、…

Linux和mysql中的基础知识

cpu读取的指令大部分在内存中&#xff08;不考虑缓存&#xff09; 任何程序在运行之前都的加入到内存。 eip->pc指针&#xff0c;指明当前指令在什么位置。 代码大概率是从上往下执行的&#xff0c;基于这样的基本理论。既可以将一部分指令加载到CPU对应的缓存中&#xf…

智能猫砂盆到底哪家好用?自费实测聚宠、糯雪、CEWEY真实反馈!

快到夏天了&#xff0c;是不是还有人因为没挑选到喜欢的智能猫砂盆而苦恼着&#xff1f;太便宜怕不好用&#xff0c;太贵怕质量比不上价格。来来去去拖到现在还没决定&#xff0c;我作为养了四年猫的资深铲屎官&#xff0c;今天就来给大家传授经验&#xff0c;关于我是怎么从好…

从源码到应用:直播电商系统与短视频带货APP开发指南

本篇文章&#xff0c;笔者将从源码到应用&#xff0c;详细探讨如何开发一个直播电商系统和短视频带货APP。 一、系统架构设计 在开始开发之前&#xff0c;首先需要对系统进行整体架构设计。一个完整的直播电商系统和短视频带货APP主要包括以下几个模块&#xff1a; 1.用户管理…

Android12 MultiMedia框架之MediaExtractorService

上节学到setDataSource()时会创建各种Source&#xff0c;source用来读取音视频源文件&#xff0c;读取到之后需要demux出音、视频、字幕数据流&#xff0c;然后再送去解码。那么负责进行demux功能的media extractor模块是在什么时候阶段创建的&#xff1f;这里暂时不考虑APP创建…