日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

  「一個優秀的開發者,一定是會利用各種工具來提升自己的開發效率。」 前段時間,博主在Gitee/Github開源了一個提升開發效率的工具,工具內集成了各種常用工具如csv、Excel、ftp、文件系統等等,「只需要簡單調用API,就可以得到想要的結果,可以極大幫助開發者提升效率」,下面來一起看看這款工具如何使用吧。

 

工具介紹

  報表的導出、導入功能、文件上傳、下載等在平常業務中是最常見不過的功能了,「許多小伙伴在開發的時候才會去網上找之前別人編寫過的案例參考,但是許多博客記載的都是時間比較長遠或者不完整的代碼,這導致在引入的時候還要處理引入的許多未知問題。」

  現在博主開源的“輪子之王”包含了這些常見的功能,「源碼全開發,每種功能都有相應的例子說明,項目會持續更新迭代,有問題還可以及時給項目提issue,相信比大多數網上的博客代碼可靠性更高。」

  「項目地址如下:」

  Github:https://github.com/it-learning-diary/it-wheels-king

  Gitee:https://gitee.com/it-learning-diary/it-wheels-king

  「項目結構如下:」

 

一、excel工具

  該工具實現采用的是開源的easyexcel框架。easyexcel是阿里的開發人員對poi框架進行了優化,解決了poi在大數據量時可能出現OOM異常,并且兼容xls和xlsx兩種文件類型的一個開源框架。

  「excel工具集成功能如下:」

  • excel的導入(可以自定義轉換后的excel數據處理的業務邏輯,支持拋出異常、事務回滾、記錄解析時的異常數據)
  • 導出(支持固定表頭,兼容多sheet頁和動態表頭,兼容多sheet頁)功能。

  「excel工具的特點如下:」

  使用過easyexcel框架的一些讀者知道,每個導入功能都要寫一個對應的Listener進行數據轉換,在很多時間其實轉換的邏輯都是類似的,不同的只不過是轉換后數據處理的業務邏輯不一樣。

  「本開源項目的excel工具則利用JAVA中的泛型和Java8中的Consumer接口將相同的部分(轉換邏輯)抽取出來,不同的部分則單獨傳入(數據處理的業務邏輯),這樣就避免了每個導入都需要創建一個相類似的Listerner,減少了類的創建和提高了開發效率。」

  「部分源碼如下:」

 /**     * 通用導入excel文件方法     *     * @param fileStream 導入的文件流     * @param rowDto 接收excel每行數據的實體     * @param rowAction 將接收到的實體進行自定義的業務處理邏輯方法     * @param <T> 實體類型     */    public static <T> void importFile(InputStream fileStream, T rowDto, ThrowingConsumer<List<T>> rowAction) {        // 獲取excel通用監聽器        ExcelImportCommonListener<T> commonListener = new ExcelImportCommonListener<>(rowAction);        // 讀取excel文件并導入        EasyExcel.read(fileStream, rowDto.getClass(), commonListener).sheet().doRead();    }    /**     * excel文件導出(可以包含多個sheet頁),固定表頭(通過實體指定屬性的方式)     * @param response     * @param fileName   導出文件名     * @param head       導出表頭(多個sheet頁就是多個集合元素)     * @param exportData 需要導出數據     * @param sheetNames sheet頁的名稱,為空則默認以:sheet + 數字規則命名     */    public static <T> void exportFile(String fileName, List<T> head, List<List<T>> exportData, List<String> sheetNames, HttpServletResponse response) {        if (Objects.isNull(response) || StrUtil.isBlank(fileName) || CollUtil.isEmpty(head)) {            log.info("ExcelExportUtil exportFile required param can't be empty");            return;        }        ExcelWriter writer = null;        try {            response.setContentType(ExportConstant.EXCEL_CONTENT_TYPE);            response.setCharacterEncoding(ExportConstant.UTF_8);            response.setHeader(ExportConstant.CONTENT_DISPOSITION, ExportConstant.ATTACHMENT_FILENAME + fileName + ExportConstant.XLSX_SUFFIX);            // 設置導出的表格樣式            HorizontalCellStyleStrategy horizontalCellStyleStrategy = getExportDefaultStyle();            writer = EasyExcel.write(response.getOutputStream()).registerWriteHandler(horizontalCellStyleStrategy).build();            for (int itemIndex = 0; itemIndex < exportData.size(); itemIndex++) {                // 表頭數據                Object headData = head.get(itemIndex);                // sheet頁的數據                List<T> list = exportData.get(itemIndex);                WriteSheet sheet = EasyExcel.writerSheet(itemIndex, CollUtil.isEmpty(sheetNames) ? ExportConstant.SHEET_NAME + itemIndex + 1 : sheetNames.get(itemIndex)).head(headData.getClass()).build();                writer.write(list, sheet);            }        } catch (Exception e) {            log.error("ExcelExportUtil exportFile in error:{}", e);        } finally {            if (null != writer) {                writer.finish();            }        }    }

  「使用案例如下(在工具中每個項目都有具體的案例,不懂的還可以留言跟博主溝通):」

/**     * 導入用戶數據案例     *     * @param file     */    @Transactional(rollbackFor = Exception.class)    public void uploadUserListDemoWithExcel(MultipartFile file, String username) throws Exception {        // 此處先校驗導入的文件類型是否為excel        String type = FileTypeUtil.getType(file.getInputStream());        if (StrUtil.isBlank(type) || type.contains(ImportConstant.XLS_TYPE) || type.contains(ImportConstant.XLSX_TYPE)) {            // 返回校驗失敗信息            return;        }        User user = new User();        user.setId(100);        user.setName("外層");        user.setPassword("外層");        userService.save(user);        // 調用統一導入方法        ExcelImportUtil.importFile(file.getInputStream(), new UserDto(), UserServiceImpl::saveUserList);    } /**     * 導出案例     *     * @param response     */    public void exportUserListDemoWithExcel(HttpServletResponse response) {        // 表頭(使用excel中的注解定義,如果表頭不固定,請使用ExcelExportUtil.exportWithDynamicData進行導出)        List<UserExportVo> head = Stream.of(new UserExportVo()).collect(Collectors.toList());        // 數據(使用兩層list為了兼容多個sheet頁,如果是不同的sheet頁則放在不同的List集合中)        List<List<UserExportVo>> exportDataList = new ArrayList<>();        List<UserExportVo> exportItem = new ArrayList<>();        // 查詢數據        List<User> dbData = userService.list();        // 將數據轉換成導出需要的實際數據格式,此處只是演示        for (User user : dbData) {            UserExportVo vo = new UserExportVo();            BeanUtil.copyProperties(user, vo);            exportItem.add(vo);        }        exportDataList.add(exportItem);        // sheet頁名稱-自定義,如果沒有則傳空        List<String> sheetNameList = Stream.of("sheet1").collect(Collectors.toList());        ExcelExportUtil.exportFile("user", head, exportDataList, sheetNameList, response);    }

二、csv工具

  Csv即逗號分隔值,也可以稱為字符分隔符,「與excel等文件相比,excel文件中會包含許多格式信息,占用的空間會更大,所以Csv在很多大數據場景導出、導入場景是非常常見的。該工具實現采用的是開源的univocity-parsers框架實現。」

  之前有一篇專門講解輪子之王項目為何使用univocity-parsers框架集成csv的詳細過程,有興趣的讀者可以點擊鏈接查看:集成csv工具的前因后果

  「部分源碼如下:」

 /**     * 使用實體bean接收csv數據文件并進行數據落盤     *     * @param inputStream     * @param errorList     * @param rowDtoClass     * @param rowAction     * @param <T>     */    public static <T> void importCsvWithBean(InputStream inputStream, List<String> errorList, Class rowDtoClass, ThrowingConsumer<List<T>> rowAction) {        // 定義bean解析者:用于將csv中數據綁定到實體屬性中,然后存儲帶list集合上        BeanListProcessor<T> rowProcessor = new BeanListProcessor<>(rowDtoClass);        CsvParserSettings setting = getDefaultSetting(errorList);        setting.setProcessor(rowProcessor);        // 創建csv文件解析        CsvParser csvParser = new CsvParser(setting);        csvParser.parse(inputStream);        // 獲取數據映射后的集合        List<T> dataList = rowProcessor.getBeans();        // 校驗必填字段        for (T row : dataList) {            // 校驗導入字段            ImportValid.validRequireField(row, errorList);        }        // 執行數據持久化        persistentBeanDataToDb(dataList, rowAction);    }       /**     * 導出csv文件(表頭和行都以實體的方式)     *     * @param response     * @param head     * @param rowDataList     */    public static <T> void exportCsvWithBean(HttpServletResponse response, String fileName, T head, List<T> rowDataList) {        CsvWriter writer = null;        try {            // 設置響應頭格式            response.setContentType(ExportConstant.EXCEL_CONTENT_TYPE);            response.setCharacterEncoding(ExportConstant.UTF_8);            response.setHeader(ExportConstant.CONTENT_DISPOSITION, ExportConstant.ATTACHMENT_FILENAME + fileName + ExportConstant.CSV_SUFFIX);            // 設置導出格式            CsvWriterSettings setting = getDefaultWriteSetting();            // 創見bean處理器,用于處理寫入數據            BeanWriterProcessor<?> beanWriter = new BeanWriterProcessor<>(head.getClass());            setting.setRowWriterProcessor(beanWriter);            // 導出數據            writer = new CsvWriter(response.getOutputStream(), setting);            writer.processRecords(rowDataList);            writer.flush();        } catch (Exception e) {            log.error("CsvExportUtil exportCsvWithBean in error:{}", e);        } finally {            if (Objects.nonNull(writer)) {                writer.close();            }        }    } 

  「使用案例如下:」

/**     * 導出案例     *     * @param response     */    public void exportUserListWithCsv(HttpServletResponse response) {        List<UserExportCsvVo> exportItem = new ArrayList<>();        // 查詢數據        List<User> dbData = userService.list();        // 使用字符串數組方式作為表頭導出csv數據        List<Object> head = Stream.of("id", "name", "password").collect(Collectors.toList());        List<List<Object>> dataList = new ArrayList<>();        for (User user : dbData) {            List<Object> row = new ArrayList<>();            row.add(user.getId());            row.add(user.getName());            row.add(user.getPassword());            dataList.add(row);        }        CsvExportUtil.exportCsvWithString(response, "demo", head, dataList);    } /**     * 導入用戶數據案例(csv模式)     *     * @param file     */@Transactional(rollbackFor = Exception.class)    public void uploadUserListWithCsv(MultipartFile file) throws Exception {        // 此處先校驗導入的文件類型是否為csv        String type = FileTypeUtil.getType(file.getInputStream());        if (StrUtil.isBlank(type) || type.contains(ImportConstant.CSV_TYPE)) {            // 返回校驗失敗信息            return;        }        User user = new User();        user.setId(100);        user.setName("外層");        user.setPassword("外層");        userService.save(user);        List<String> errorLogList = new ArrayList<>();        // 調用統一導入方法        // 方式一:使用csv數據映射到dto實體的方式進行數據導入        //CsvImportUtil.importCsvWithBean(file.getInputStream(), errorLogList, UserCsvDto.class, UserServiceImpl::saveUserListWithCsv);        // 方式二、使用csv數據映射到字符串數組的方式進行數據導入        CsvImportUtil.importCsvWithString(file.getInputStream(), errorLogList, UserCsvDto.class, UserServiceImpl::saveUserListWithCsvStringArrDemo);        // 如果存在解析異常,輸出解析異常并進行事務回滾        if (CollUtil.isNotEmpty(errorLogList)) {            throw new RuntimeException(StrUtil.toString(errorLogList));        }    }

三、ftp工具

  Ftp文件上傳下載相比excel、csv等出現的場景較少,「但是,如果你參與的項目是政府或者涉及到第三方舊系統對接的時候,很多時候就需要使用到它。因為很多舊系統或者政府項目使用的技術比較舊或者有制度限制,一般都是以文件的形式與你進行交互,此時ftp工具就很有效了。」

  Ftp工具使用的commons.NET開源框架進行實現,具體的集成流程之前單獨使用一篇文章進行了非常詳細的介紹,有需要的讀者可以點擊后面鏈接查看:手把手教你搭建ftp服務器,并用程序完成ftp上傳下載功能

  「部分源碼如下:」

/**     * 上傳     *     * @return     */    public boolean upload(FtpUploadParam param) {        boolean flag = false;        FTPClient ftpClient = new FTPClient();        //1 測試連接        if (connect(ftpClient, param.getHostname(), param.getPort(), param.getUsername(), param.getPassword())) {            try {                //2 檢查工作目錄是否存在,不存在則創建                if (!ftpClient.changeWorkingDirectory(param.getWorkingPath())) {                    ftpClient.makeDirectory(param.getWorkingPath());                }                // 將文件編碼成Ftp服務器支持的編碼類型(FTP協議里面,規定文件名編碼為iso-8859-1,所以目錄名或文件名需要轉碼。)                String fileName = new String(param.getSaveName().getBytes(ftpClientCharset), ftpServerCharset);                // 3 上傳文件                if (ftpClient.storeFile(fileName, param.getInputStream())) {                    flag = true;                } else {                    log.warn("FtpUtils uploadFile unsuccessfully!!");                }            } catch (IOException e) {                log.error("FtpUtils upload in error:{}", e);            } finally {                disconnect(ftpClient);            }        }        return flag;    }/**     * @description: 下載ftp文件     * @param:     * @param: param     * @param: downloadFileName     * @return:     * @date: 2022/7/14 10:56     */    public boolean download(FtpDownloadParam param, String downloadFileName) {        FTPClient ftpClient = new FTPClient();        FileOutputStream out = null;        boolean downloadResult = false;        //1 測試連接        if (connect(ftpClient, param.getHostname(), param.getPort(), param.getUsername(), param.getPassword())) {            try {                String localPath = param.getDownloadPath() + param.getFileName();                out = new FileOutputStream(new File(localPath));                //2 檢查工作目錄是否存在,不存在返回                // if (!ftpClient.changeWorkingDirectory(param.getWorkingPath())) {                //    return false;                // }                /*                 * 打開FTP服務器的PASS模式(不記得FTP協議支持的模式請翻到文章第一階段)                 * 這個方法的意思就是每次數據連接之前,ftp client告訴ftp server開通一個端口來傳輸數據. 因為ftp                 * server可能每次開啟不同的端口來傳輸數據,但是在linux上,由于安全限制,可能某些端口沒有開啟,可能出現出現阻塞                 */                ftpClient.enterLocalPassiveMode();                // 設置文件的傳輸方式-二進制                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);                // 將文件編碼成Ftp服務器支持的編碼類型(FTP協議里面,規定文件名編碼為iso-8859-1,所以目錄名或文件名需要轉碼。)                // 缺少編碼轉換會導致:從FTP服務器下載下來的文件是破損的,無法被打開                downloadResult = ftpClient.retrieveFile(new String(downloadFileName                        .getBytes(ftpClientCharset), ftpServerCharset), out);                out.flush();            } catch (IOException e) {                log.error("FtpUtils upload in error:{}", e);                return false;            } finally {                try {                    if (Objects.nonNull(out)) {                        out.close();                    }                } catch (Exception e) {                    log.error("FtpUtils upload in error:{}", e);                }                disconnect(ftpClient);            }        }        return downloadResult;    }

  「具體使用案例如下:」

@PostMApping("/ftp/upload")    public void upload() {        try {            FtpUploadParam param = new FtpUploadParam();            param.setHostname(ftpConfig.getServerHostname());            param.setPort(ftpConfig.getServerPort());            param.setUsername(ftpConfig.getServerUsername());            param.setPassword(ftpConfig.getServerPassword());            param.setWorkingPath(ftpConfig.getServerWorkingPath());            param.setSaveName("xxx.mp3");            InputStream in = new FileInputStream(new File("D:/uploadfile/like.mp3"));            param.setInputStream(in);            ftpUtils.upload(param);        } catch (Exception e) {            log.error("TestFtpServerController upload 錯誤:{}", e);        }    }    @PostMapping("/ftp/download")    public void download() {        try {            FtpDownloadParam param = new FtpDownloadParam();            param.setHostname(ftpConfig.getServerHostname());            param.setPort(ftpConfig.getServerPort());            param.setUsername(ftpConfig.getServerUsername());            param.setPassword(ftpConfig.getServerPassword());            param.setWorkingPath(ftpConfig.getServerWorkingPath());            param.setDownloadPath("D:/downloadFile/");            param.setFileName("xxx.mp3");            ftpUtils.download(param, "xxxx.mp3");        } catch (Exception e) {            log.error("TestFtpServerController download 錯誤:{}", e);        }    }

三、分布式文件系統工具

  「非結構化數據通常是使用文件的方式進行存儲,這時候不可避免地要使用到文件系統進行管理。」 分布式文件系統工具使用了第三方開源框架seaweedfs進行搭建,可以實現程序上傳,刪除、下載、查詢,并有文件分布式存儲,避免單點故障,節約成本等特點。

  前面也專門通過一篇文章講解了:為何要使用seaweedfs框架搭建分布式文件系統的,感興趣的讀者可以通過下方鏈接進行查看:Gitee圖床崩潰后,我使用Seaweedfs搭建了文件系統并封裝成輪子開源

  「部分源碼如下:」

/**     * @description: 上傳單個文件到文件服務器     * @param: file     * @return: 文件的fid + 文件的請全訪問地址     * @author: it     */    public String uploadFile(MultipartFile file) throws Exception {        FileSource fileSource = getFileSource();        FileTemplate fileTemplate = new FileTemplate(fileSource.getConnection());        // 上傳文件        FileHandleStatus handleStatus = fileTemplate.saveFileByStream(file.getOriginalFilename(), file.getInputStream(), contentType);        // 獲取上傳文件的訪問地址        String fileUrl = fileTemplate.getFileUrl(handleStatus.getFileId());        // 關閉當前連接        fileSource.shutdown();        return handleStatus.getFileId() + StrUtil.DASHED + fileUrl;    }/**     * @description: 根據文件下載文件     * @param: fid     * @param: response     * @param: fileName     * @author: it     */    public void downloadFileByFid(HttpServletResponse response, HttpServletRequest request, String fid, String fileName) throws Exception {        FileSource fileSource = getFileSource();        FileTemplate fileTemplate = new FileTemplate(fileSource.getConnection());        StreamResponse fileStream = fileTemplate.getFileStream(fid);        // 設置響應頭        response.setContentType(CommonConstant.CONTENT_TYPE);        response.setCharacterEncoding(CommonConstant.UTF_8);        String encodeFileName = buildingFileNameAdapterBrowser(request, fileName);        response.setHeader(CommonConstant.CONTENT_DISPOSITION, CommonConstant.ATTACHMENT_FILENAME + encodeFileName);        // 讀取并寫入到響應輸出        InputStream inputStream = fileStream.getInputStream();        byte[] fileByte = new byte[inputStream.available()];        inputStream.read(fileByte);        response.getOutputStream().write(fileByte);        response.getOutputStream().flush();        fileSource.shutdown();    }

  「具體使用案例如下:」

/**     * @description: 上傳文件     * @param:     * @param: file     * @return:     * @author: it     * @date: 2022/7/14 17:01     */    @ResponseBody    @RequestMapping("upload")    public void uploadFile(MultipartFile file) {        try {            String fileUrl = seaweedFsUtil.uploadFile(file);            System.out.println(fileUrl);        } catch (Exception e) {            log.error("TestSeaweedFsController uploadFile in error:{}", e);        }    }    /**     * @description: 下載文件     * @param:     * @param: fileId     * @return:     * @author: it     * @date: 2022/7/14 17:01     */    @RequestMapping("download")    public void downloadFile(HttpServletResponse response, HttpServletRequest request, String fileId, String fileName) {        try {            seaweedFsUtil.downloadFileByFid(response, request, fileId, fileName);        } catch (Exception e) {            log.error("TestSeaweedFsController downloadFile in error:{}", e);        }    }

工具更新

  到此為止,輪子之王已集成的工具就介紹完畢了,后續還會不斷更新、集成新的輪子,下面給大家介紹一下下一段時間項目的一些工作(「如果讀者有想要集成的輪子,歡迎提issue或者這文章下面留言」):

  • 集成一個可視化界面,更好地介紹開源工具中各個輪子的引入案例,方便大家使用。
  • 集成word文件導出工具
  • 集成pdf文件導出工具
  • 集成復雜報表的報表導出工具(使用freemaker框架)
  • 待更新...

集成方案介紹關聯文章

  • 手把手教你搭建ftp服務器,并用程序完成ftp上傳下載功能
  • 集成csv工具的前因后果
  • Gitee圖床崩潰后,我使用Seaweedfs搭建了文件系統并封裝成輪子開源

寫在最后

  開源之路不容易,開源之心不忘記!「如果博主開源的項目對您有所幫助,請給項目star,給博主更多動力,如果閱讀文章給您有所幫助,請給博主點贊、關注。」

  該開源項目會持續更新和維護,希望有更多讀者能夠提出建議和想法,

分享到:
標簽:開源 工具
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定