日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

导出(若依框架)

發布時間:2023/12/10 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 导出(若依框架) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

導出(若依框架)

分析用戶界面,以用戶列表的導出為例

導出

前端代碼

? 點擊導出按鈕,觸發函數handleExport,在該函數中調用exportUser,exportUser執行完畢后,再調用download方法下載。

exportUser執行完成后,后端會生成臨時文件execl。再調用download下載該文件。

/** 導出按鈕操作 */ handleExport() {const queryParams = this.queryParams;this.$confirm('是否確認導出所有用戶數據項?', "警告", {confirmButtonText: "確定",cancelButtonText: "取消",type: "warning"}).then(function() {return exportUser(queryParams);}).then(response => {this.download(response.msg);}) }, // 導出用戶 export function exportUser(query) {return request({url: '/system/user/export',method: 'get',params: query}) } // 通用下載方法 export function download(fileName) {window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true; }

后端代碼

? 完成導出共發起了兩次請求。分別是生成文件和下載文件

? 技術:反射,注解

文件生成

主要分析以下幾個方法。方法調用層級關系

export 導出的入口函數

調用userService.selectUserList方法查詢需要導出的數據,再調用util.exportExcel生成execl文件。

@Log(title = "用戶管理", businessType = BusinessType.EXPORT) @PreAuthorize("@ss.hasPermi('system:user:export')") @GetMapping("/export") public AjaxResult export(SysUser user) {List<SysUser> list = userService.selectUserList(user);// 創建 ExcelUtil<SysUser>對象,入參為 SysUser.class。ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);return util.exportExcel(list, "用戶數據"); }
exportExcel

在exportExcel方法中調用了init方法和exportExcel方法

public AjaxResult exportExcel(List<T> list, String sheetName) {this.init(list, sheetName, Type.EXPORT);return exportExcel(); }
init

在init中調用了createExcelField方法,主要完成對ExcelUtil類中的fields屬性賦值。

public void init(List<T> list, String sheetName, Type type) {if (list == null){list = new ArrayList<T>();}this.list = list; // 需要導出的數據交給listthis.sheetName = sheetName; // 生成execl的sheet名稱this.type = type; // 類型(0:導出導入;1:僅導出;2:僅導入)createExcelField(); // 主要完成對 List<Object[]> fields 屬性的賦值。createWorkbook(); // 創建 Workbook對象 Workbook wb = new SXSSFWorkbook(500) }
createExcelField

該方法執行完成后,完成了對ExcelUtil對象中的LIst<Object[]> fields屬性的賦值。fields存放了導出的信息。

在object[]數組,object[0]存放了java.lang.reflect.Field對象, object[1]存放了注解com.ruoyi.common.annotation.Excel對象。

從object[0]可以獲取到字段名稱等信息。 從object[1]中可以獲取到導出到execl中的名稱以及對該字段的值作何處理(如格式化)等信息。

private void createExcelField() {this.fields = new ArrayList<Object[]>();List<Field> tempFields = new ArrayList<>();// clazz屬性是創建ExcelUtil對象時,完成了對該屬性的賦值。以用戶導出為例,class=SysUser.class。// 獲取該類和其父類的屬性字段,存入到tempFields集合中。tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));// 遍歷字段,過濾出符合規律的字段。// 規律:1. 如果該字段有注解@Excel,則將字段對象,和注解對象封裝到object[]數組中,并add到fields集合中。// 規律:2. 如果該字段有@Excels注解,則從該注解對象中獲取Excel[]數組進行遍歷。并將字段對象和Excel對象封裝后,add到 // fields集合中。for (Field field : tempFields){// 單注解if (field.isAnnotationPresent(Excel.class)){// this.fields.add(new Object[] { field, attr });putToField(field, field.getAnnotation(Excel.class));}// 多注解if (field.isAnnotationPresent(Excels.class)){Excels attrs = field.getAnnotation(Excels.class);Excel[] excels = attrs.value();for (Excel excel : excels){putToField(field, excel);}}}this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());this.maxHeight = getRowHeight(); }
exportExcel 完成execl文件的生成
/*** 對list數據源將其里面的數據導入到excel表單* * @return 結果*/ public AjaxResult exportExcel() {OutputStream out = null;try{// 算出一共有多少個sheet. // list.size 需要導出的數據條數。 sheetSize = 65536double sheetNo = Math.ceil(list.size() / sheetSize);for (int index = 0; index <= sheetNo; index++){// 創建sheet頁createSheet(sheetNo, index);// 產生一行,表頭Row row = sheet.createRow(0);int column = 0;// 寫入各個字段的列頭名稱// 遍歷fields集合,該集合的元素為object[]類型。從os[1]中獲取注解對象,創建表頭信息。for (Object[] os : fields){Excel excel = (Excel) os[1];// 創建單元格,并賦值,完成表頭的創建this.createCell(excel, row, column++);}// 如果為導出類型,調用fillExcelData方法填充excel數據。if (Type.EXPORT.equals(type)){// 填充數據fillExcelData(index, row);addStatisticsRow();}}// 生成文件名稱String filename = encodingFilename(sheetName);// 生成的文件路徑在application.yml配置 ( profile: D:/ruoyi/uploadPath)out = new FileOutputStream(getAbsoluteFile(filename));// 生成execl文件,此時生成的文件在服務端。wb.write(out);// 將生成的文件名稱封裝到AjaxResult對象中return AjaxResult.success(filename);}catch (Exception e){log.error("導出Excel異常{}", e.getMessage());throw new CustomException("導出Excel失敗,請聯系網站管理員!");}finally{// 省略} }
fillExcelData 完成execl數據填充
/*** 填充excel數據* * @param index 序號* @param row 單元格行*/ public void fillExcelData(int index, Row row) {// 以第一個sheet頁為例 index = 0, 常量sheetSize=65536// startNo是數據開始下標int startNo = index * sheetSize;// endNo-1是數據結束下標int endNo = Math.min(startNo + sheetSize, list.size());for (int i = startNo; i < endNo; i++){// 創建行對象,每一個sheet頁從第二行開始,第一行為標題行。row = sheet.createRow(i + 1 - startNo);// 得到導出對象.T vo = (T) list.get(i);int column = 0;// 遍歷fieldsfor (Object[] os : fields){Field field = (Field) os[0];Excel excel = (Excel) os[1];// 設置實體類私有屬性可訪問field.setAccessible(true);// 將導出信息 和 數據對象 execl的行對象 交由 addCell處理。this.addCell(excel, row, vo, field, column++);}} }
addCell 完成對行記錄的填充。

創建單元格,填充單元格內容。

/*** 添加單元格 */ public Cell addCell(Excel attr, Row row, T vo, Field field, int column) {Cell cell = null;try{// 設置行高row.setHeight(maxHeight);// 根據Excel中設置情況決定是否導出,有些情況需要保持為空,希望用戶填寫這一列.if (attr.isExport()){// 創建cellcell = row.createCell(column);int align = attr.align().value();cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));// 用于讀取對象中的屬性// 通過Object o = field.get(vo); 得到字段的屬性對象。// 如果注解屬性targetAttr為空,直接返回o.// 若果不為空,該字段是否有小數點為標準再做處理// 該方法詳解見下文。Object value = getTargetValue(vo, field, attr);// 字段的日期格式String dateFormat = attr.dateFormat();// 讀取內容轉表達式 (如: 0=男,1=女,2=未知)String readConverterExp = attr.readConverterExp();// 分隔符,讀取字符串組內容String separator = attr.separator();// 字典類型 (如: sys_user_sex)String dictType = attr.dictType();// 根據字段的處理策略。填充單元格。if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)){cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));}else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)){cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));}else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)){cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));}else if (value instanceof BigDecimal && -1 != attr.scale()){cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());}else{// 設置列類型setCellVo(value, attr, cell);}addStatisticsData(column, Convert.toStr(value), attr);}}catch (Exception e){log.error("導出Excel失敗{}", e);}return cell; }
getTargetValue 獲取bean中的屬性值
/*** 獲取bean中的屬性值* * @param vo 實體對象* @param field 字段* @param excel 注解* @return 最終的屬性值* @throws Exception*/ private Object getTargetValue(T vo, Field field, Excel excel) throws Exception {// 通過反射獲取字段的值。該值有可能是其他類的對象。Object o = field.get(vo);// 如果 excel.targetAttr()不為空,則說明o是其他類中的一個對象。if (StringUtils.isNotEmpty(excel.targetAttr())){// 獲取注解 targetAttr屬性(另一個類中的屬性名稱,支持多級獲取,以小數點隔開)String target = excel.targetAttr();if (target.indexOf(".") > -1){// 如果該屬性有小數點,分割為數組遍歷。 // 多級獲取邏輯 // 舉例說明:A類中持有B類的對象,B類中持有C類的對象。 導出A類數據時,需要導出C類的一個屬性值// 可以使用.隔開。String[] targets = target.split("[.]");for (String name : targets){o = getValue(o, name);}}else{// 如果不包含小數點,(o, target) o為其他類對象,target為該類中的字段名稱值。 // getValue 是通過o.getClass獲取class對象,通過target字段名稱從class中獲取到Filed對象。進而得到filed字段值// 例子:SysUser#dept字段。o = getValue(o, target);}}return o; }/*** 以類的屬性的get方法方法形式獲取值* * @param o* @param name* @return value* @throws Exception*/ private Object getValue(Object o, String name) throws Exception {if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)){Class<?> clazz = o.getClass();Field field = clazz.getDeclaredField(name);field.setAccessible(true);o = field.get(o);}return o; }

小結

自定義注解,描述Bean字段在Execl的表現形式。(比如,字段的值否需要格式化,字段對應到表格中的列名稱等等)

使用List<object[]> 存入類的Filed對象和注解對象。(比如導出的字段有10個,則該集合大小為10)

導出執行的大致邏輯:

根據注解信息完成List<object[]>集合的賦值。

根據導出的數據量,計算需要導出的sheet頁。針對每一個sheet頁進行處理

創建表頭:遍歷List<object[]>集合,通過object[1]得到字段的注解信息,創建sheet頁表頭。

填充數據: 根據sheet頁數,計算對應的數據范圍,循環創建行對象,根據循環下標,在數據集合中獲取改行對應的數據對象。行中創建單元格對象,接著遍歷List<object[]>集合,通過object[0]獲取數據對象中屬性值,通過object[1]>獲取對該值的處理策略。處理完畢后,將值填充到單元格中。

文件下載

文件生成后,將生成的文件名稱返回到前端,客戶端在調用download方法,向后端發起下載請求。

fileDownload下載入口方法
/*** 通用下載請求* * @param fileName 文件名稱* @param delete 是否刪除*/ @GetMapping("common/download") public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) {try{if (!FileUtils.checkAllowDownload(fileName)){throw new Exception(StringUtils.format("文件名稱({})非法,不允許下載。 ", fileName));}String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);// 獲取下載路勁String filePath = RuoYiConfig.getDownloadPath() + fileName;// 設置ContentTyp="application/octet-stream" 通用Mime類型response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);// 設置Content-disposition FileUtils.setAttachmentResponseHeader(response, realFileName);FileUtils.writeBytes(filePath, response.getOutputStream());// 下載后是否刪除該文件if (delete){FileUtils.deleteFile(filePath);}}catch (Exception e){log.error("下載文件失敗", e);} }

總結

以上是生活随笔為你收集整理的导出(若依框架)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。