EasyExcel Export
EasyExcel Export

Импортировать зависимости

зависимости EasyExcel

Язык кода:javascript
копировать
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>3.1.1</version>
    </dependency>	

зависимости Hutool

Язык кода:javascript
копировать
    <dependency>
		<groupId>cn.hutool</groupId>
		<artifactId>hutool-core</artifactId>
		<version>5.6.5</version>
	</dependency>

Официальная демо-версия

Официальный-Быстрый старт

Экспорт по шаблону

Диаграмма шаблона

Код инструмента

Заполнение шаблона для экспорта информации о клиентах CRM

Язык кода:javascript
копировать
/**
 * Экспортируйте информацию о клиентах CRM
 *
 * @param list1     Список 1
 * @param list2     Список 2
 * @param dataMap  карта данных
 * @param xlsxName Имя файла
 */
public static void exportCrmUserInfoList(String path, String xlsxName, List<?> list1, List<?> list2, Map<Object, Object> dataMap) throws IOException {
    //TODO В будущем оно будет изменено на динамическое приобретение.
    String templateFileName = "/Users/xguo/Desktop/template2.xlsx";
    String fileName = path + "/" + xlsxName + ".xlsx";
    //Получаем экземпляр ExcelWriter
    ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build();
    WriteSheet writeSheet = EasyExcel.writerSheet().build();
    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
    //Заполняем соответствующую переменную
    excelWriter.fill(dataMap, writeSheet);
    //заполняем список
    excelWriter.fill(new FillWrapper("data1",list1),fillConfig, writeSheet);
    excelWriter.fill(new FillWrapper("data2",list2), fillConfig,writeSheet);
    //Закрываем поток
    excelWriter.finish();
}

Путь к шаблону можно разместить в ресурсах проекта или настроить через центр конфигурации nacos, что более гибко. Для удобства тестирования он прописан в коде.

Список сжатых файлов — zip (копия Baidu).

Язык кода:javascript
копировать
/**
 * Сжатый список файлов
 *
 * @param zipOutName   путь вывода zip-файла
 * @param fileNameList список путей к файлам
 * @throws IOException          исключение ввода-вывода
 * @throws ExecutionException   Исключение выполнения
 * @throws InterruptedException Исключение прерывания
 */
public static void compressFileList(String zipOutName, List<String> fileNameList) throws IOException, ExecutionException, InterruptedException {
    ThreadFactory factory = new ThreadFactoryBuilder().setNamePrefix("compressFileList-pool-").build();
    ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), factory);
    ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executor);
    OutputStream outputStream = new FileOutputStream(zipOutName);
    ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
    zipArchiveOutputStream.setEncoding("UTF-8");
    for (String fileName : fileNameList) {
        File inFile = new File(fileName);
        final InputStreamSupplier inputStreamSupplier = () -> {
            try {
                return new FileInputStream(inFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return new NullInputStream(0);
            }
        };
        ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(inFile.getName());
        zipArchiveEntry.setMethod(ZipArchiveEntry.DEFLATED);
        zipArchiveEntry.setSize(inFile.length());
        zipArchiveEntry.setUnixMode(UnixStat.FILE_FLAG | 436);
        parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
    }
    parallelScatterZipCreator.writeTo(zipArchiveOutputStream);
    zipArchiveOutputStream.close();
    outputStream.close();
    log.info("ParallelCompressUtil->ParallelCompressUtil-> info:{}", JSONObject.toJSONString(parallelScatterZipCreator.getStatisticsMessage()));
}

Выходной файл передается во внешний интерфейс для загрузки.

Язык кода:javascript
копировать
/**
 * Выходной файл передается во внешний интерфейс для загрузки.
 *
 * @param filePath путь к файлу
 * @param response ответ
 */
public static void exportFileStream(String filePath, HttpServletResponse response) {
    File file = new File(filePath);
    if (!file.exists()) {
        //Файл не существует, подсказка 404 - Запрошенный ресурс (веб-страница и т. д.) не существует.
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
    }
    try (FileInputStream is = new FileInputStream(file); OutputStream os = response.getOutputStream()) {
        response.setCharacterEncoding(CharEncoding.UTF_8);
        response.addHeader("Content-Disposition", "attachment;filename=" + file.getName().getBytes());
        response.addHeader("Content-Length", "" + file.length());
        response.setContentType("application/x-zip-compressed");
        os.write(IoUtil.readBytes(is));
    } catch (IOException e) {
        log.error("Передача файла не удалась: {}", e.getMessage());
    }
}

Код класса сущности

Переменные соответствуют классам сущностей.

Язык кода:javascript
копировать
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * Объект просмотра экспорта информации о пользователе crm
 *
 * @author xGuo
 * @date 2022/06/28
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("Объект просмотра экспорта информации о пользователе crm")
public class CrmUserInfoExportVo {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty("Счет фонда клиента")
    private String account;

    @ApiModelProperty("Номер мобильного телефона клиента")
    private String phone;

    @ApiModelProperty("Контактный адрес")
    private String address;

    @ApiModelProperty("Номер удостоверения личности")
    private String idCard;

    @ApiModelProperty("Срок действия удостоверения личности")
    private String idCardValidTime;

    @ApiModelProperty("серийный номер")
    private String id;

    @ApiModelProperty("Состояние контракта")
    private String state;

    @ApiModelProperty("Срок действия контракта")
    private String contractValidTime;
}

Соответствует Диаграмме шаблонанаОсновная информация о клиенте Информация о контракте переменная

Список заполняется соответствующим классом сущности.

Язык кода:javascript
копировать
import lombok.Data;

import java.util.Date;

/**
 * объект заполнения списка
 *
 * @author xGuo
 * @date 2022/06/28
 */
@Data
public class ListFill {
    /**
     * Название 1
     */
    private String title;
    /**
     * Сообщество
     */
    private String room;
    /**
     * Название 2
     */
    private String title1;
    /**
     * Время работы
     */
    private Date openTime;
    /**
     * Название 3
     */
    private String title2;
    /**
     * Имя пользователя
     */
    private String user;
    /**
     * Примечание
     */
    private String remarks;
}

Соответствует Диаграмме шаблонанаИнформация о разрешении Примечания и требования соответствия переменная

код бизнес-логики

Сервисный уровень

Язык кода:javascript
копировать
/**
 * экспорт информации о пользователях crm
 *
 * @param response ответ
 * @param ids      id
 * @throws IOException          исключение ввода-вывода
 * @throws ExecutionException   Исключение выполнения
 * @throws InterruptedException Исключение прерывания
 */
void crmUserInfoExport(HttpServletResponse response , List<Long> ids) throws IOException, ExecutionException, InterruptedException;

Слой ServiceImpl

Язык кода:javascript
копировать
@Override
public void crmUserInfoExport(HttpServletResponse response, List<Long> ids) throws IOException, ExecutionException, InterruptedException {
    if (ids != null && ids.size() > 0) {
        //Фиксированный каталог
        String parentPath = "/Users/xguo/Desktop/" + "crmFile/";
        // String path = PropertiesConfig.UPLOAD_PATH + "/crmFile/";
        String childPath = String.valueOf(System.currentTimeMillis());
        //путь к хранилищу
        String path = parentPath + childPath;
        //Создаем, если он не существует
        File pathFile = new File(path);
        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }
        try {
            for (Long id : ids) {
                //Получать Информация о контракте
                UserCustomAuthorize validContractInfo = userCustomAuthorizeMapper.getUserValidContractInfo(id);
                //Получать Информация о разрешении
                List<RoomAuthorize> authorizeList = roomAuthorizeMapper.getByUserId(id);
                //Получать合同Примечание
                List<String> remarksList = userCustomAuthorizeMapper.getRemarksByUserId(id);
                try {
                    //Экспорт заполненного шаблона Excel
                    EasyExcelUtil.exportCrmUserInfoList(path, validContractInfo.getName(), date1(authorizeList), date2(remarksList), getMap(validContractInfo));
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
            //Получать文件目录下的список путей к файлам
            List<String> listFileNames = FileUtil.listFileNames(path);
            List<String> fileNames = new ArrayList<>();
            for (String fileName : listFileNames) {
                fileNames.add(path + "/" + fileName);
            }
            //Сжатый список файлов
            EasyExcelUtil.compressFileList(path + ".zip", fileNames);
            //Выходной файл передается во внешний интерфейс для загрузки.
            EasyExcelUtil.exportFileStream(path + ".zip", response);
        }catch (Exception e){
            log.error("Не удалось загрузить информацию о клиенте CRM, причина сбоя: {}",e.getMessage());
        }finally {
            //удаляем файл
            FileUtil.del(path + ".zip");
            FileUtil.del(path);
        }
    }
}

Методы, связанные с Слоем ServiceImpl

Язык кода:javascript
копировать
/**
 * мономерпеременнаякарта данных
 *
 * @param validContractInfo эффективный Информация о контракте
 * @return {@link Map}<{@link Object}, {@link Object}>
 */
private Map<Object, Object> getMap(UserCustomAuthorize validContractInfo) {
    Map<Object, Object> map = MapUtils.newHashMap();
    //Основная информация о клиенте
    map.put("account", validContractInfo.getFuturesAccount());
    map.put("phone", validContractInfo.getPhone());
    map.put("address", validContractInfo.getAddress());
    map.put("idCard", validContractInfo.getIdCard());
    map.put("idCardValidTime", validContractInfo.getEndTime());
    //Информация о контракте
    map.put("id", validContractInfo.getId());
    map.put("state", validContractInfo.getAuthState());
    map.put("contractValidTime", validContractInfo.getEndTime());
    return map;
}



/**
 * Информация о список разрешении данных
 *
 * @param authorizeList Список полномочий
 * @return {@link List}<{@link ListFill}>
 */
private List<ListFill> date1(List<RoomAuthorize> authorizeList) {
    List<ListFill> data1 = new ArrayList<>();
    for (int i = 0; i < authorizeList.size(); i++) {
        ListFill fill = new ListFill();
        fill.setTitle("Открыть Сообщество"+(i+1));
        fill.setRoom(authorizeList.get(i).getRoomName());
        fill.setTitle1("Дата открытия");
        fill.setOpenTime(authorizeList.get(i).getAddTime());
        fill.setTitle2("Открывалка");
        fill.setUser(authorizeList.get(i).getAddPerson());
        data1.add(fill);
    }
    return data1;
}


//    /**
//     * Информация о список разрешении данных
//     *
//     * @param authorizeList Список полномочий
//     * @return {@link List}<{@link ListFill}>
//     */
//    private List<ListFill> date1(List<RoomAuthorize> authorizeList) {
//        List<ListFill> data1 = new ArrayList<>();
//        if (authorizeList != null && authorizeList.size() > 0) {
//            for (int i = 0; i < authorizeList.size(); i++) {
//                ListFill fill = new ListFill();
//                fill.setTitle("Открыть Сообщество" + (i + 1));
//                fill.setRoom(Optional.of(authorizeList.get(i).getRoomName()).orElse(""));
//                fill.setTitle1("Дата открытия");
//                fill.setOpenTime(Optional.of(authorizeList.get(i).getAddTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString()).orElse(""));
//                fill.setTitle2("Открывалка");
//                fill.setUser(Optional.of(authorizeList.get(i).getAddPerson()).orElse(""));
//                data1.add(fill);
//            }
//        }
//        return data1;
//    }

/**
 * Примечание список данных
 *
 * @param remarksList Примечаниесписок
 * @return {@link List}<{@link ListFill}>
 */
private List<ListFill> date2(List<String> remarksList) {
    List<ListFill> data2 = new ArrayList<>();
    remarksList.forEach(remarks -> {
        ListFill fill = new ListFill();
        fill.setRemarks(remarks);
        data2.add(fill);
    });
    return data2;
}

Уровень контроллера

Язык кода:javascript
копировать
/**
 * Экспортируйте информацию о клиентах CRM
 */
@GetMapping("/exportCrm")
public void exportCrm(HttpServletResponse response,@RequestParam("ids") List<Long> ids) throws IOException, ExecutionException, InterruptedException {
    long startTime=System.currentTimeMillis();
    iUsersService.crmUserInfoExport(response, ids);
    long endTime=System.currentTimeMillis();
    System.out.println("Время работы программы: "+(endTime-startTime)+"ms");
}

Экспорт отображения Excel

Экспортируйте несколько листов из разных объектов

Экспортировать класс сущности

основной класс сущности

Язык кода:javascript
копировать
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import io.swagger.annotations.ApiModel;
import lombok.*;

import java.util.Date;

/**
 * Объект представления экспорта статистики авторизации запроса
 *
 * @author xGuo
 * @date 2022/06/30
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("Объект представления экспорта статистики авторизации запроса")
public class AuthorizeContractExportVo {

    private static final long serialVersionUID = 1L;

    @ExcelProperty(value = «Идентификатор договора», заказ = 1)
    @ColumnWidth(15)
    private Long id;

    @ExcelProperty(value = "имя пользователя", заказ = 2)
    @ColumnWidth(15)
    private String name;

    @ExcelProperty(value = «Счет Фонда», заказ = 3)
    @ColumnWidth(15)
    private String futuresAccount;

    @ExcelProperty(value = «Дата подписания», приказ = 4)
    @ColumnWidth(25)
    private Date addTime;

    @ExcelProperty(value = «Срок годности», заказ = 5)
    @ColumnWidth(25)
    private Date endTime;
}

Дочерний класс сущности (добавьте поля расширения)

Язык кода:javascript
копировать
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import io.swagger.annotations.ApiModel;
import lombok.*;

import java.util.Date;

/**
 * Объект представления экспорта статистики авторизации запроса
 *
 * @author xGuo
 * @date 2022/06/30
 */
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("Объект представления экспорта статистики авторизации запроса")
public class AuthorizeCancelExportVo extends AuthorizeContractExportVo {

    @ExcelProperty(value = "Отписаться/время прекращения подписки",заказать = 6)
    @ColumnWidth(25)
    private Date updateTime;

    @ExcelProperty(value = «Причина отписки/прекращения», заказ = 7)
    @ColumnWidth(25)
    private String remarks;
}

Метод реализации

Язык кода:javascript
копировать
@Override
    public void ExportAuthorizeCount (ответ HttpServletResponse) {
        //Устанавливаем входной поток,Настроить домен ответа
        response.setCharacterEncoding(CharEncoding.UTF_8);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

        //карта статуса
        Map<String, String> stateMap = new LinkedHashMap<>();
        stateMap.put("all", «Всего авторизованных пользователей»);
        stateMap.put("valid", «действительный пользователь»);
        stateMap.put("noRoom", «Нет Сообществопользователя»);
        stateMap.put("soon", «Пользователи, срок действия которых истекает»);
        stateMap.put("expire7", «Пользователь с истекшим сроком действия (7 дней)»);
        stateMap.put("expire30", «Пользователь с истекшим сроком действия (30 дней)»);
        stateMap.put("stop", «завершить пользователя»);
        stateMap.put("cancel", «Отписаться от пользователя»);

        // Разные объекты записываются на разные листы
        try (ExcelWriter excelWriter = EasyExcel.write(new BufferedOutputStream(response.getOutputStream()), AuthorizeContractExportVo.class).build()) {
            int index = 0;
            for (String key : stateMap.keySet()) {
                //Получаем соответствующий список данных по статусу
                List<UserCustomAuthorize> authorizes = getBaseMapper().getByAuthState(key);
                //Отдельная обработка прекращения и отписки
                if ("stop".equals(key) || "cancel".equals(key)) {
                //Обратите внимание на метод .head(). Это ключ к вставке различных классов сущностей. Без использования этого метода невозможно получить расширенные поля.
                    WriteSheet build = EasyExcel.writerSheet(index, stateMap.get(key)).head(AuthorizeCancelExportVo.class).build();
                    List<AuthorizeCancelExportVo> cancelExportVos = BeanUtil.copyToList(authorizes, AuthorizeCancelExportVo.class, new CopyOptions());
                    excelWriter.write(cancelExportVos, build);
                }
                WriteSheet writeSheet = EasyExcel.writerSheet(index, stateMap.get(key)).build();
                List<AuthorizeContractExportVo> exportVos = BeanUtil.copyToList(authorizes, AuthorizeContractExportVo.class, new CopyOptions());
                excelWriter.write(exportVos, writeSheet);

                index++;
            }
        } catch (IOException e) {
            log.error("export error:{}",e.getMessage());
        }
    }

Экспорт схемы

в соответствии сосновной класс сущности Экспортированоsheet

Лист экспортирован по классу подобъекта

Другие методы экспорта

Пока предстоит определиться…

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose