Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 欧美成人免费高清二区三区,在线观看精品视频,中国一级淫片

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          java導(dǎo)出大量的excel

          java導(dǎo)出大量的excel

          話少說,直入主題

          基本思路為 創(chuàng)建一個(gè)臨時(shí)文件 寫入數(shù)據(jù) 導(dǎo)出數(shù)據(jù) 刪除臨時(shí)文件

          首先需要兩個(gè)jar包

          antlr和stringtemplate

          創(chuàng)建數(shù)據(jù)庫中的類Row

           private String name1;
           
           private String name2;
           
           private String name3;
           public String getName1() {
           return name1;
           }
           public void setName1(String name1) {
           this.name1=name1;
           }
           public String getName2() {
           return name2;
           }
           public void setName2(String name2) {
           this.name2=name2;
           }
           public String getName3() {
           return name3;
           }
           public void setName3(String name3) {
           this.name3=name3;
           }
          


          然后需要?jiǎng)?chuàng)建對(duì)應(yīng)的Worksheet類

          <span style="font-size:10px;">private int columnNum;
           private int rowNum;
           private List<Row> rows;
           private List<SfOrder> orders;
           public List<SfOrder> getOrders() {
           return orders;
           }
           public void setOrders(List<SfOrder> orders) {
           this.orders=orders;
           }
           public String getSheet() {
           return sheet;
           }
           public void setSheet(String sheet) {
           this.sheet=sheet;
           }
           public List<Row> getRows() {
           return rows;
           }
           public void setRows(List<Row> rows) {
           this.rows=rows;
           }
           public int getColumnNum() {
           return columnNum;
           }
           public void setColumnNum(int columnNum) {
           this.columnNum=columnNum;
           }
           public int getRowNum() {
           return rowNum;
           }
           public void setRowNum(int rowNum) {
           this.rowNum=rowNum;
           }</span>
          

          然后需要寫兩個(gè)文件分別為

          head.st

          <?xml version="1.0"?>
          <?mso-application progid="Excel.Sheet"?>
          <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
           xmlns:o="urn:schemas-microsoft-com:office:office"
           xmlns:x="urn:schemas-microsoft-com:office:excel"
           xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
           xmlns:html="http://www.w3.org/TR/REC-html40">
           <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
           <Created>1996-12-17T01:32:42Z</Created>
           <LastSaved>2013-08-02T09:21:24Z</LastSaved>
           <Version>11.9999</Version>
           </DocumentProperties>
           <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
           <RemovePersonalInformation/>
           </OfficeDocumentSettings>
           <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
           <WindowHeight>4530</WindowHeight>
           <WindowWidth>8505</WindowWidth>
           <WindowTopX>480</WindowTopX>
           <WindowTopY>120</WindowTopY>
           <AcceptLabelsInFormulas/>
           <ProtectStructure>False</ProtectStructure>
           <ProtectWindows>False</ProtectWindows>
           </ExcelWorkbook>
           <Styles>
           <Style ss:ID="Default" ss:Name="Normal">
           <Alignment ss:Vertical="Bottom"/>
           <Borders/>
           <Font ss:FontName="宋體" x:CharSet="134" ss:Size="12"/>
           <Interior/>
           <NumberFormat/>
           <Protection/>
           </Style>
           </Styles>
          和body.st
           $worksheet:{
           <Worksheet ss:Name="$it.sheet$">
           <Table ss:ExpandedColumnCount="$it.columnNum$" ss:ExpandedRowCount="$it.rowNum$" x:FullColumns="1"
           x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="14.25">
           $it.rows:{
           <Row>
           <Cell><Data ss:Type="String">$it.name1$</Data></Cell>
           </Row>
           }$
           </Table>
           </Worksheet>
          }$
          

          下面就是程序代碼

          首先你要從數(shù)據(jù)中查詢數(shù)據(jù),假設(shè)你查詢的數(shù)據(jù)為List數(shù)據(jù)

           //我這里的list是從map中獲取的你可以直接獲取
           List<Row> list=(List<Row>) result.get("list");
           list=(List<SfOrder>) result.get("list");
           if(list.size()==0){
           return null;
           }
           long startTimne=System.currentTimeMillis();
           StringTemplateGroup stGroup=new StringTemplateGroup("stringTemplate"); 
           stGroup.setFileCharEncoding("UTF-8");//設(shè)置編碼,否則有亂碼,導(dǎo)出excel格式不正確
           //寫入excel文件頭部信息
           StringTemplate head=stGroup.getInstanceOf("/template/head");
           String path=request.getSession().getServletContext().getRealPath("/upload/excel/test1.xls");
           File file=new File(path);
           PrintWriter writer=new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
           writer.print(head.toString());
           writer.flush();
           int sheets=10;
           //excel單表最大行數(shù)是65535
           int maxRowNum=3000;
           int maxRowNumLast=0;
           //計(jì)算要分幾個(gè)sheet
           sheets=(list.size()%maxRowNum==0) ? list.size()/maxRowNum:list.size()/maxRowNum+1;
           //計(jì)算最后一個(gè)sheet有多少行
           maxRowNumLast=list.size()-(sheets-1)*maxRowNum;
          上面為預(yù)備階段,下面則直接向文件寫數(shù)據(jù)
          //寫入excel文件數(shù)據(jù)信息
           for(int i=0;i<sheets;i++){
           Integer nums=0;
           StringTemplate body=stGroup.getInstanceOf("/template/body");
           Worksheet worksheet=new Worksheet();
           worksheet.setSheet(" "+(i+1)+" ");
           worksheet.setColumnNum(3);
           worksheet.setRowNum(maxRowNum);
           List<Row> orders=new ArrayList<Row>();
           if(i==(sheets-1)){
           for(int j=0;j<maxRowNumLast;j++){
           nums=i*maxRowNumLast+j;
           SfOrder order=new SfOrder();
           order.setOrderId(list.get(nums).getOrderId());
           orders.add(order);
           }
           }else{
           for(int j=0;j<maxRowNum;j++){
           nums=i*maxRowNum+j;
           Row order=new Row();
           order.setOrderId(list.get(nums).getOrderId());
           orders.add(order);
           }
           }
           worksheet.setOrders(orders);
           body.setAttribute("worksheet", worksheet);
           writer.print(body.toString());
           writer.flush();
           orders.clear();
           orders=null;
           worksheet=null;
           body=null;
           Runtime.getRuntime().gc();
           System.out.println("正在生成excel文件的 sheet"+(i+1));
           }
          下面則寫入服務(wù)器磁盤
          //寫入excel文件尾部
           writer.print("</Workbook>");
           writer.flush();
           writer.close();
           System.out.println("生成excel文件完成");
           long endTime=System.currentTimeMillis();
           System.out.println("用時(shí)="+((endTime-startTimne)/1000)+"秒");
          下面要讀取文件,即正式的導(dǎo)出
           //創(chuàng)建file對(duì)象
           File upload=new File(path);
           //設(shè)置response的編碼方式
           response.setContentType("application/vnd.ms-excel");
           response.setHeader("Content-Disposition","attachment;filename=ceshi.xls");
           //讀出文件到i/o流
           FileInputStream fis=new FileInputStream(upload);
           BufferedInputStream buff=new BufferedInputStream(fis);
           byte [] b=new byte[1024];//相當(dāng)于我們的緩存
           long k=0;//該值用于計(jì)算當(dāng)前實(shí)際下載了多少字節(jié)
           //從response對(duì)象中得到輸出流,準(zhǔn)備下載
           OutputStream myout=response.getOutputStream();
           //開始循環(huán)下載
           while(k<upload.length()){
           int j=buff.read(b,0,1024);
           k+=j;
           //將b中的數(shù)據(jù)寫到客戶端的內(nèi)存
           myout.write(b,0,j);
           }
           //將寫入到客戶端的內(nèi)存的數(shù)據(jù),刷新到磁盤
           myout.flush();
           myout.close();
          如果需要可以,比如這個(gè)文件是動(dòng)態(tài)的我們可以讓這個(gè)文件是動(dòng)態(tài)的,就要將此文件刪除
           File fileDel=new File(path); 
           // 如果文件路徑所對(duì)應(yīng)的文件存在,并且是一個(gè)文件,則直接刪除 
           if (fileDel.exists() && file.isFile()) { 
           fileDel.delete()
           }
          

          這樣就大功告成啦,導(dǎo)出所用的時(shí)間大部分是花費(fèi)在數(shù)據(jù)庫查詢中,當(dāng)然這也是你數(shù)據(jù)庫功底的問題啦

          篇文章主要介紹使用 exceljs、file-saverjszip實(shí)現(xiàn)下載包含多層級(jí)文件夾、多個(gè) excel、每個(gè) excel 支持多個(gè) sheet 的 zip 壓縮包。上一篇文章:前端復(fù)雜表格導(dǎo)出excel,一鍵導(dǎo)出 Antd Table 看這篇就夠了(附源碼)[1]詳細(xì)介紹了如何實(shí)現(xiàn)解析 Antd Table、組裝數(shù)據(jù)和調(diào)整表格的樣式,感興趣的可以先看看。本篇將接著上一篇,重點(diǎn)講方法的更高級(jí)抽象,和下載多層級(jí)文件夾的 zip 壓縮包。源碼地址:github.com/cachecats/e…[2]

          實(shí)現(xiàn)效果

          最終下載的是 壓縮包.zip,解壓之后包含多個(gè)文件夾,每個(gè)文件夾下又可以無限嵌套子文件夾,excel 文件可以自由選擇放到根目錄下,或者子文件夾下。實(shí)現(xiàn)效果如圖:

          使用方法

          使用方式也很簡單,經(jīng)過高度封裝后,只需按照方法參數(shù)的規(guī)則傳入?yún)?shù)即可:

          downloadFiles2ZipWithFolder({
                zipName: '壓縮包',
                folders: [
                  {
                    folderName: '文件夾1',
                    files: [
                      {
                        filename: 'test',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                      {
                        filename: 'test2',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                    ]
                  },
                  {
                    folderName: '文件夾2',
                    files: [
                      {
                        filename: 'test',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                      {
                        filename: 'test2',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                    ]
                  },
                  {
                    folderName: '文件夾2/文件夾2-1',
                    files: [
                      {
                        filename: 'test',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                      {
                        filename: 'test2',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                    ]
                  },
                  {
                    folderName: '文件夾2/文件夾2-1/文件夾2-1-1',
                    files: [
                      {
                        filename: 'test',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                      {
                        filename: 'test2',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                    ]
                  },
                  {
                    folderName: '',
                    files: [
                      {
                        filename: 'test',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        },
                          {
                            sheetName: 'test2',
                            columns: columns,
                            dataSource: list
                          }
                        ]
                      },
                      {
                        filename: 'test2',
                        sheets: [{
                          sheetName: 'test',
                          columns: columns,
                          dataSource: list
                        }]
                      },
                    ]
                  }
                ]
              })
          復(fù)制代碼

          這里會(huì)封裝三個(gè)方法,分別滿足不同場景下的導(dǎo)出需求:

          • downloadExcel:導(dǎo)出普通的單文件 excel,預(yù)設(shè)樣式,可包含多個(gè) sheet。
          • downloadFiles2Zip:將多個(gè) excel 文件導(dǎo)出到一個(gè) zip 壓縮包內(nèi),沒有嵌套文件夾。
          • downloadFiles2ZipWithFolder:導(dǎo)出包含多級(jí)子文件夾、每級(jí)包含多個(gè) excel 文件的 zip 壓縮包。

          一、封裝普通的下載導(dǎo)出 excel 方法

          我們來封裝一個(gè)常用的,預(yù)定義好樣式,直接能開箱即用的導(dǎo)出方法,使用者不用關(guān)心具體細(xì)節(jié),只管簡單的調(diào)用:

          function onExportExcel() {
            downloadExcel({
              filename: 'test',
              sheets: [{
                sheetName: 'test',
                columns: columns,
                dataSource: list
              }]
            })
          }
          復(fù)制代碼

          如上,直接調(diào)用 downloadExcel方法,它傳入一個(gè)對(duì)象作為參數(shù),分別有 filenamesheets兩個(gè)屬性。

          • filename:文件名。不用帶 .xlsx后綴,會(huì)自動(dòng)加后綴名。
          • sheets:sheet 數(shù)組。傳入幾個(gè) sheet 對(duì)象就會(huì)創(chuàng)建幾個(gè) sheet 頁。

          Sheet對(duì)象的定義:

          export interface ISheet {
            // sheet 的名字
            sheetName: string;
            // 這個(gè) sheet 中表格的 column,類型同 antd 的 column
            columns: ColumnType<any>[];
            // 表格的數(shù)據(jù)
            dataSource: any[];
          }
          復(fù)制代碼

          核心代碼

          downloadExcel方法關(guān)鍵源碼:

          export interface IDownloadExcel {
            filename: string;
            sheets: ISheet[];
          }
          
          export interface ISheet {
            // sheet 的名字
            sheetName: string;
            // 這個(gè) sheet 中表格的 column,類型同 antd 的 column
            columns: ColumnType<any>[];
            // 表格的數(shù)據(jù)
            dataSource: any[];
          }
          
          /**
           * 下載導(dǎo)出簡單的表格
           * @param params
           */
          export function downloadExcel(params: IDownloadExcel) {
            // 創(chuàng)建工作簿
            const workbook = new ExcelJs.Workbook();
            params?.sheets?.forEach((sheet) => handleEachSheet(workbook, sheet));
            saveWorkbook(workbook, `${params.filename}.xlsx`);
          }
          
          
          function handleEachSheet(workbook: Workbook, sheet: ISheet) {
            // 添加sheet
            const worksheet = workbook.addWorksheet(sheet.sheetName);
            // 設(shè)置 sheet 的默認(rèn)行高。設(shè)置默認(rèn)行高跟自動(dòng)撐開單元格沖突
            // worksheet.properties.defaultRowHeight = 20;
            // 設(shè)置列
            worksheet.columns = generateHeaders(sheet.columns);
            handleHeader(worksheet);
            handleData(worksheet, sheet);
          }
          
          
          export function saveWorkbook(workbook: Workbook, fileName: string) {
            // 導(dǎo)出文件
            workbook.xlsx.writeBuffer().then((data: any) => {
              const blob = new Blob([data], {type: ''});
              saveAs(blob, fileName);
            });
          }
          復(fù)制代碼

          generateHeaders方法是設(shè)置表格的列。handleHeader方法負(fù)責(zé)處理表頭,設(shè)置表頭的高度、背景色、字體等樣式。handleData方法處理每一行具體的數(shù)據(jù)。這三個(gè)方法的實(shí)現(xiàn)在上篇文章都有介紹,如需了解更多請(qǐng)查看源碼:github.com/cachecats/e…[3]

          導(dǎo)出的 excel 效果如下圖,列寬會(huì)根據(jù)傳入的 width 動(dòng)態(tài)計(jì)算,單元格高度會(huì)根據(jù)內(nèi)容自動(dòng)撐開。

          二、導(dǎo)出包含多個(gè) excel 的 zip 壓縮包

          如果沒有多級(jí)目錄的需求,只想把多個(gè) excel 文件打包到一個(gè)壓縮包里,可以用 downloadFiles2Zip這個(gè)方法,得到的目錄結(jié)構(gòu)如下圖:

          參數(shù)結(jié)構(gòu)如下,支持導(dǎo)出多個(gè) excel 文件,每個(gè) excel 文件又可以包含多個(gè) sheet。

          export interface IDownloadFiles2Zip {
            // 壓縮包的文件名
            zipName: string;
            files: IDownloadExcel[];
          }
          
          export interface IDownloadExcel {
            filename: string;
            sheets: ISheet[];
          }
          
          export interface ISheet {
            // sheet 的名字
            sheetName: string;
            // 這個(gè) sheet 中表格的 column,類型同 antd 的 column
            columns: ColumnType<any>[];
            // 表格的數(shù)據(jù)
            dataSource: any[];
          }
          復(fù)制代碼

          使用示例

          function onExportZip() {
            downloadFiles2Zip({
              zipName: '壓縮包',
              files: [
                {
                  filename: 'test',
                  sheets: [
                    {
                      sheetName: 'test',
                      columns: columns,
                      dataSource: list
                    },
                    {
                      sheetName: 'test2',
                      columns: columns,
                      dataSource: list
                    }
                  ]
                },
                {
                  filename: 'test2',
                  sheets: [{
                    sheetName: 'test',
                    columns: columns,
                    dataSource: list
                  }]
                },
                {
                  filename: 'test3',
                  sheets: [{
                    sheetName: 'test',
                    columns: columns,
                    dataSource: list
                  }]
                }
              ]
            })
          }
          復(fù)制代碼

          核心代碼

          通過 handleEachFile()方法處理每個(gè) fille 對(duì)象,每個(gè) file 其實(shí)就是一個(gè) excel 文件,即一個(gè) workbook。給每個(gè) excel 創(chuàng)建 workbook并將數(shù)據(jù)寫入,然后通過 JsZip庫寫入到壓縮文件內(nèi),最終用 file-saver庫提供的 saveAs方法導(dǎo)出壓縮文件。注意 12、13行,handleEachFile()方法返回的是一個(gè) Promise,需要等所有異步方法都執(zhí)行完之后再執(zhí)行下面的生成 zip 方法,否則可能會(huì)遺漏文件。

          import {saveAs} from 'file-saver';
          import * as ExcelJs from 'exceljs';
          import {Workbook, Worksheet, Row} from 'exceljs';
          import JsZip from 'jszip'
          
          /**
           * 導(dǎo)出多個(gè)文件為zip壓縮包
           */
          export async function downloadFiles2Zip(params: IDownloadFiles2Zip) {
            const zip = new JsZip();
            // 待每個(gè)文件都寫入完之后再生成 zip 文件
            const promises = params?.files?.map(async param => await handleEachFile(param, zip, ''))
            await Promise.all(promises);
            zip.generateAsync({type: "blob"}).then(blob => {
              saveAs(blob, `${params.zipName}.zip`)
            })
          }
          
          async function handleEachFile(param: IDownloadExcel, zip: JsZip, folderName: string) {
            // 創(chuàng)建工作簿
            const workbook = new ExcelJs.Workbook();
            param?.sheets?.forEach((sheet) => handleEachSheet(workbook, sheet));
            // 生成 blob
            const data = await workbook.xlsx.writeBuffer();
            const blob = new Blob([data], {type: ''});
            if (folderName) {
              zip.folder(folderName)?.file(`${param.filename}.xlsx`, blob)
            } else {
              // 寫入 zip 中一個(gè)文件
              zip.file(`${param.filename}.xlsx`, blob);
            }
          }
          
          function handleEachSheet(workbook: Workbook, sheet: ISheet) {
            // 添加sheet
            const worksheet = workbook.addWorksheet(sheet.sheetName);
            // 設(shè)置 sheet 的默認(rèn)行高。設(shè)置默認(rèn)行高跟自動(dòng)撐開單元格沖突
            // worksheet.properties.defaultRowHeight = 20;
            // 設(shè)置列
            worksheet.columns = generateHeaders(sheet.columns);
            handleHeader(worksheet);
            handleDataWithRender(worksheet, sheet);
          }
          復(fù)制代碼

          render 渲染的單元格處理

          數(shù)據(jù)處理還有一點(diǎn)需要注意,因?yàn)橛械膯卧袷峭ㄟ^ render 函數(shù)渲染的,render 函數(shù)里可能進(jìn)行了一系列復(fù)雜的計(jì)算,所以如果 column 中有 render 的話不能直接以 dataIndex 為 key 進(jìn)行取值,要拿到 render 函數(shù)執(zhí)行后的值才是正確的。比如 Table 的 columns 如下:

          const columns: ColumnsType<any> = [
              {
                width: 50,
                dataIndex: 'id',
                key: 'id',
                title: 'ID',
                render: (text, row) => <div><p>{row.id + 20}</p></div>,
              },
              {
                width: 100,
                dataIndex: 'name',
                key: 'name',
                title: '姓名',
              },
              {
                width: 50,
                dataIndex: 'age',
                key: 'age',
                title: '年齡',
              },
              {
                width: 80,
                dataIndex: 'gender',
                key: 'gender',
                title: '性別',
              },
            ];
          復(fù)制代碼

          第一列傳入了 render 函數(shù) render: (text, row)=> <div><p>{row.id + 20}</p></div>,經(jīng)過計(jì)算后,ID 列顯示的值應(yīng)該是原來的 id + 20。構(gòu)造的數(shù)據(jù)原來的 id 是 0-4,頁面上顯示的應(yīng)該是 20-24,如下圖:

          這時(shí)導(dǎo)出的 excel 應(yīng)該跟頁面上顯示的一模一樣,這樣才是正確的。點(diǎn)擊【導(dǎo)出zip】按鈕,解壓后打開下載的其中一個(gè) excel,驗(yàn)證顯示的內(nèi)容跟在線表格完全一致。

          那么是如何做到的呢?主要看 handleDataWithRender()方法:

          /**
           * 如果 column 有 render 函數(shù),則以 render 渲染的結(jié)果顯示
           * @param worksheet
           * @param sheet
           */
          function handleDataWithRender(worksheet: Worksheet, sheet: ISheet) {
            const {dataSource, columns} = sheet;
            const rowsData = dataSource?.map(data => {
              return columns?.map(column => {
                // @ts-ignore
                const renderResult = column?.render?.(data[column.dataIndex], data);
                if (renderResult) {
                  // 如果不是 object 說明沒包裹標(biāo)簽,是基本類型直接返回
                  if (typeof renderResult !== "object") {
                    return renderResult;
                  }
                  // 如果是 object 說明包裹了標(biāo)簽,逐級(jí)取出值
                  return getValueFromRender(renderResult);
                }
                // @ts-ignore
                return data[column.dataIndex];
              })
            })
            // 添加行
            const rows = worksheet.addRows(rowsData);
            // 設(shè)置每行的樣式
            addStyleToData(rows);
          }
          
          
          // 遞歸取出 render 里的值
          // @ts-ignore
          function getValueFromRender(renderResult: any) {
            if (renderResult?.type) {
              let children = renderResult?.props?.children;
              if (children?.type) {
                return getValueFromRender(children);
              } else {
                return children;
              }
            }
            return ''
          }
          復(fù)制代碼

          worksheet.addRows()可以添加數(shù)據(jù)對(duì)象,也可以添加由每行的每列組成的二維數(shù)組。由于我們要自己控制每個(gè)單元格顯示的內(nèi)容,所以采用第二種方式,傳入一個(gè)二維數(shù)組來構(gòu)造 row。結(jié)構(gòu)如下圖所示:

          循環(huán) dataSourcecolumns,就得到了每個(gè)單元格要顯示的內(nèi)容,通過執(zhí)行 render 函數(shù),得到 render 執(zhí)行后的結(jié)果:const renderResult=column?.render?.(data[column.dataIndex], data);注意 render 需要傳入兩個(gè)參數(shù),一個(gè)是 text,一個(gè)是這行的數(shù)據(jù)對(duì)象,我們都能確定參數(shù)的值,所以直接傳入。然后判斷 renderResult的類型,如果是 object 類型,說明是個(gè)由 html 標(biāo)簽包裹的 ReactNode,需要遞歸取出最終渲染的值。如果是非 object 類型,說明是 boolean 或者 string 這樣的基本類型,即沒有被標(biāo)簽包裹,可以直接展示。由于我們采用了遞歸來取最后渲染的值,所以無論嵌套了多少層標(biāo)簽,都可以正確的取到值。

          三、導(dǎo)出包含多個(gè)子文件夾、多個(gè)excel文件的 zip 壓縮包

          如果文件、文件夾嵌套比較深,可以使用 downloadFiles2ZipWithFolder()方法。文件結(jié)構(gòu)如下圖:

          核心代碼

          export interface IDownloadFiles2ZipWithFolder {
            zipName: string;
            folders: IFolder[];
          }
          
          export interface IFolder {
            folderName: string;
            files: IDownloadExcel[];
          }
          
          export interface IDownloadExcel {
            filename: string;
            sheets: ISheet[];
          }
          
          export interface ISheet {
            // sheet 的名字
            sheetName: string;
            // 這個(gè) sheet 中表格的 column,類型同 antd 的 column
            columns: ColumnType<any>[];
            // 表格的數(shù)據(jù)
            dataSource: any[];
          }
          
          
          /**
           * 導(dǎo)出支持多級(jí)文件夾的壓縮包
           * @param params
           */
          export async function downloadFiles2ZipWithFolder(params: IDownloadFiles2ZipWithFolder) {
            const zip = new JsZip();
            const outPromises = params?.folders?.map(async folder => await handleFolder(zip, folder))
            await Promise.all(outPromises);
            zip.generateAsync({type: "blob"}).then(blob => {
              saveAs(blob, `${params.zipName}.zip`)
            })
          }
          
          async function handleFolder(zip: JsZip, folder: IFolder) {
            console.log({folder})
            let folderPromises: Promise<any>[] = [];
            const promises = folder?.files?.map(async param => await handleEachFile(param, zip, folder.folderName));
            await Promise.all([...promises, ...folderPromises]);
          }
          復(fù)制代碼

          跟上一個(gè)方法 downloadFiles2Zip相比,參數(shù)的數(shù)據(jù)結(jié)構(gòu)多了層 folders,其他的邏輯基本沒變。所以 downloadFiles2ZipWithFolder方法能實(shí)現(xiàn)downloadFiles2Zip方法的所有功能。

          使用示例

          如文章開頭的使用示例,為了方便看清結(jié)構(gòu),將每個(gè)對(duì)象的 files 值刪除,精簡之后得到如下結(jié)構(gòu):

          downloadFiles2ZipWithFolder({
                zipName: '壓縮包',
                folders: [
                  {
                    folderName: '文件夾1',
                    files: []
                  },
                  {
                    folderName: '文件夾2',
                    files: []
                  },
                  {
                    folderName: '文件夾2/文件夾2-1',
                    files: []
                  },
                  {
                    folderName: '文件夾2/文件夾2-1/文件夾2-1-1',
                    files: []
                  },
                  {
                    folderName: '',
                    files: []
                  }
                ]
              })
          復(fù)制代碼

          不管嵌套幾層文件夾,folders永遠(yuǎn)是一個(gè)一維數(shù)組,每一項(xiàng)里面也不會(huì)嵌套 folders。多級(jí)目錄是通過文件名 folderName實(shí)現(xiàn)的。

          • folderName為空字符串,則將它的 files放入壓縮包的頂級(jí)目錄中,不在任何子文件內(nèi)。
          • folderName為普通字符串,如:文件夾1,則以 folderName為文件名新建一個(gè)文件夾,并將它的 files放入此文件夾下。
          • folderName為帶斜杠的字符串,如:文件夾2/文件夾2-1/文件夾2-1-1,則按照順序依次新建 n 個(gè)文件夾并保持嵌套關(guān)系,最終將它的files放入最后一個(gè)文件夾下。

          如需查看 demo 完整代碼,源碼地址:github.com/cachecats/e…[4]

          我的博客即將同步至騰訊云+社區(qū),邀請(qǐng)大家一同入駐:cloud.tencent.com/developer/s…[5]


          關(guān)于本文

          作者:solocoder

          https://juejin.cn/post/7080169896209809445

          述可幫助您找到構(gòu)建可處理大量數(shù)據(jù)的Web應(yīng)用程序的正確解決方案。

          在本文中,我將基于其功能和許可策略簡要概述五個(gè)流行的獨(dú)立JavaScript電子表格庫。這些圖書館可以免費(fèi)在非營利項(xiàng)目中實(shí)施,也可以提供商業(yè)用途的付費(fèi)許可。我希望此概述可以幫助您找到構(gòu)建旨在處理大量數(shù)據(jù)的Web應(yīng)用程序的正確解決方案。

          Handsontable

          Handsontable據(jù)說是一個(gè)帶有電子表格外觀的JavaScript網(wǎng)格。它是一個(gè)純JavaScript庫,支持React,Vue.js和Angular。其基本功能列表包括折疊列,調(diào)整大小,移動(dòng)和隱藏列和行,添加注釋,顯示列摘要,導(dǎo)出數(shù)據(jù),應(yīng)用條件格式,使用數(shù)據(jù)驗(yàn)證和添加下拉菜單的功能。也可以對(duì)數(shù)據(jù)進(jìn)行排序和過濾并使用自動(dòng)填充。

          更有趣的是高級(jí)功能列表。例如,開發(fā)人員可以選擇在觸發(fā)表呈現(xiàn)時(shí)應(yīng)使用哪個(gè)渲染器。此外,還可以創(chuàng)建自定義插件,使用自定義按鈕,并在單元格之間定義自定義邊框。此外,還有諸如多列排序,嵌套標(biāo)題,修剪行 等功能。

          Handsontable提供 三種類型的許可證:非商業(yè)免費(fèi)許可證,開發(fā)人員(每個(gè)開發(fā)人員790美元),企業(yè)(自定義價(jià)格)許可證。

          我的結(jié)論:Handsontable是非商業(yè)項(xiàng)目和那些準(zhǔn)備為豐富功能付費(fèi)的人的一個(gè)很好的選擇。

          AG-網(wǎng)格

          ag-Grid是一個(gè)JavaScript網(wǎng)格/電子表格組件,可以與Angular,Angular JS 1.x,React,Vue.js,Polymer和Web Components輕松集成,沒有第三方依賴。這個(gè)100,000行的演示判斷它的快速性能和數(shù)十 行。

          分組和聚合功能允許用戶以他們想要的方式處理數(shù)據(jù)??梢园刺囟袑?duì)數(shù)據(jù)進(jìn)行分組,并且可以在分組行中顯示各種聚合列值。ag-Grid提供快速過濾功能和自定義過濾器。延遲加載允許在用戶滾動(dòng)時(shí)顯示所需的行數(shù)并請(qǐng)求其他數(shù)據(jù),這有助于節(jié)省服務(wù)器資源。ag-Grid支持實(shí)時(shí)更新,每秒可處理數(shù)百個(gè)更新。您可以閱讀有關(guān)這些功能和其他功能的更多信息,并在功能概述頁面上查看一些演示 。

          該庫有 兩個(gè)版本:社區(qū)和企業(yè)。社區(qū)版本由MIT許可證涵蓋,僅包括基本功能。具有所有可用功能的Enterprise許可證有三個(gè)選項(xiàng):Single Application Developer(每個(gè)開發(fā)人員750美元),Multiple Application Developer(每個(gè)開發(fā)人員1,200美元)和部署許可證(每個(gè)生產(chǎn)環(huán)境750美元)。

          我的結(jié)論是:ag-Grid提供了許多有用的功能,并且可以與不同的JS框架進(jìn)行簡單的集成。對(duì)于大預(yù)算項(xiàng)目來說,這似乎是一個(gè)不錯(cuò)的選擇,因?yàn)樵S可非常靈活而且價(jià)格昂貴。

          dhtmlxSpreadsheet

          dhtmlxSpreadsheet是一個(gè)可自定義的JavaScript電子表格組件,具有Material skin和類似Excel的界面。它為三個(gè)流行的框架提供了包裝器:React,Vue.js和Angular。

          您可以根據(jù)其用戶指南自定義此電子表格的幾乎每個(gè)元素- 例如,使用工具欄,菜單和上下文菜單控件的自定義圖標(biāo)字體包而不是Material-design控件。單元格格式功能允許您更改文本顏色和裝飾,背景顏色,設(shè)置文本對(duì)齊,將不同的數(shù)字格式應(yīng)用于單元格值(百分比,貨幣甚至自定義格式),調(diào)整列大小等。

          還可以鎖定單元格,通過在單元格中鍵入數(shù)據(jù)來自動(dòng)填充包含內(nèi)容的單元格,并拖動(dòng)填充句柄以延長其他單元格中的一系列數(shù)字或字母。dhtmlxSpreadsheet提供了相當(dāng)多的導(dǎo)航熱鍵列表。此外,該庫提供了從Excel文件導(dǎo)入和導(dǎo)出數(shù)據(jù)的機(jī)會(huì)。為此,dhtmlxSpreadsheet開發(fā)人員已經(jīng)實(shí)現(xiàn)了他們自己的開源庫Excel2Json和Json2Excel。

          dhtmlxSpreadsheet提供四個(gè)主要許可證:免費(fèi)GNU GPL v2,商業(yè)許可證(最多5個(gè)開發(fā)人員149美元),企業(yè)許可證(最多20個(gè)開發(fā)人員團(tuán)隊(duì)449美元),終極許可證(無限數(shù)量的開發(fā)人員669美元)。

          我的判斷:dhtmlxSpreadsheet提供了一組基本功能和對(duì)流行框架的支持,可以被認(rèn)為是物有所值的。

          Clusterize.js

          開發(fā)人員將Clusterize.js描述 為一個(gè)很小的插件,可以輕松顯示大型數(shù)據(jù)集。它的權(quán)重僅為2.3KB gzipped,但遺憾的是它沒有提供任何高級(jí)功能。其主要目的是使具有大量行的表在網(wǎng)頁上平滑運(yùn)行。Clusterize.js不是用所有使用的標(biāo)簽“污染”DOM,而是將列表拆分為簇,然后顯示當(dāng)前滾動(dòng)位置的元素,并在列表的頂部和底部添加額外的行以模擬表的完整高度,以便瀏覽器顯示完整列表的滾動(dòng)條。此庫適用于現(xiàn)代瀏覽器,并支持所有主要的移動(dòng)設(shè)備。

          好消息是Clusterize.js非常實(shí)惠。在 可用的許可證中,您可以找到個(gè)人許可證(個(gè)人項(xiàng)目免費(fèi)),商業(yè)許可證(25美元用于商業(yè)用途和無限數(shù)量的項(xiàng)目),以及擴(kuò)展許可證(110美元;可以包含在待售產(chǎn)品中)。

          我的結(jié)論是:Clusterize.js是那些尋找能夠快速運(yùn)行并節(jié)省一些錢的單一用途工具的人的絕佳選擇。

          SlickGrid

          SlickGrid是一個(gè)簡潔而簡約的JavaScript電子表格組件。自適應(yīng)虛擬滾動(dòng)允許處理數(shù)十萬行而沒有任何延遲。該庫支持jQuery UI主題并支持廣泛的自定義。用戶可以調(diào)整列的大小,重新排序,顯示或隱藏,使用分組,過濾,自定義聚合器和其他功能??刹迦雴卧窀袷狡骱途庉嬈髟试S您擴(kuò)展Web應(yīng)用程序的功能。

          如您所見,SlickGrid提供了一組基本功能,可以滿足普通用戶的需求。不幸的是,根據(jù)項(xiàng)目的 GitHub頁面,這個(gè)庫最近沒有受到開發(fā)人員的太多關(guān)注。好消息是SlickGrid是免費(fèi)提供的。

          我的判斷:如果你不是在尋找豐富的功能或者買不起商業(yè)許可證,那么SlickGrid可能是個(gè)不錯(cuò)的選擇。


          主站蜘蛛池模板: av一区二区三区人妻少妇| 日韩在线一区二区| 人妻少妇精品一区二区三区| 国产免费私拍一区二区三区| 日韩美女在线观看一区| 日韩精品人妻av一区二区三区| 国产一区二区精品久久岳√| 无码日韩人妻AV一区二区三区| 国产一区三区三区| 日韩一区精品视频一区二区| 免费无码A片一区二三区| 视频一区二区在线观看| 性色AV一区二区三区| 在线精品亚洲一区二区| 日本一区二区三区在线网| 国产成人一区二区三区在线| 久久精品亚洲一区二区三区浴池 | 日本人真淫视频一区二区三区| 成人在线视频一区| 亚洲高清日韩精品第一区| 精品日韩一区二区| 亚洲一区二区三区在线观看精品中文 | 亚洲av无码天堂一区二区三区| 国产AV一区二区三区无码野战 | 日韩精品中文字幕视频一区| 精品一区二区三区免费视频| 亚洲一区二区三区四区视频| 一区二区三区在线视频播放| 国产成人精品日本亚洲专一区| 精品一区二区三区AV天堂| 农村乱人伦一区二区| 国产一区二区高清在线播放| 中文字幕AV无码一区二区三区| 中文字幕一区二区三区四区| bt7086福利一区国产| 国产成人AV一区二区三区无码| 波多野结衣在线观看一区二区三区| 亚洲福利秒拍一区二区| 成人无码一区二区三区| 国产一区二区三区免费在线观看| 人妻激情偷乱视频一区二区三区 |