jfinal layui easyexcel 這三樣開源技術這里就不多介紹了,自行百度了解吧,他們的組合算是一個很高效又不失美觀的操作體驗。
操作主要分以下幾步:
1、建立jfinal的操作環境,建議使用作者提供的demo , 創建一個 jfinal + undertow 的運行環境。undertow運行起來很快,不僅方便調試,而且運行穩定。https://www.jfinal.com/doc
2、引入easyexcel的相關包,我這里使用的是2.0.5版本,網上有很多1.2的版本教程,這里使用的最新的版本,同老版本還是有些區別的,作者提供了相關的demo,可以自己試一下。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>test</scope>
</dependency>
3、建立你要導入的數據庫表,我這里用的是mysql5.7,並編寫你的導入模板。這里就不貼圖了,自己親手做下就行。
4、分別創建幾個類文件 jfinal的controller 、service ,easyexcel使用的 excelData、excelDataListener 文件,當然還有前端訪問的excelupload.html頁面。
easyexcel 使用 Data文件來約束模板文件的標題(頭文件),導入字段的類型和excel中cell的順序
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ContentRowHeight; import com.alibaba.excel.annotation.write.style.HeadRowHeight; import lombok.Data; /** * 基礎數據類.用於基礎數據表的導入 * 這里的排序和excel里面的排序一致 * * @author **/ @Data //使用注解來格式該文件 @HeadRowHeight(20) // 作為導出data的時候設置頭文件行高 @ContentRowHeight(20) //作為導出data的時候設置內容行高 @ColumnWidth(25)//設置行寬 public class ExcelData { @ExcelProperty(value = "工號", index = 0) //這里的工號是和excel的標題一致,index 表示第幾列數據 private String agentcode; @ExcelProperty(value = "姓名", index = 1) private String name; @ExcelProperty(value = "手機號", index = 2) private String mobile; @ExcelProperty(value = "身份證號", index = 3) private String idno; @ExcelProperty(value = "執業證號", index = 4) private String certifno; @ExcelProperty(value = "機構代碼", index = 5) private String agentgroupcode; @ExcelProperty(value = "機構名稱", index = 6) private String agentgroupname; public String getAgentcode() { return agentcode; } public void setAgentcode(String agentcode) { this.agentcode = agentcode; } public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } public String getIdno() { return idno; } public void setIdno(String idno) { this.idno = idno; } public String getCertifno() { return certifno; } public void setCertifno(String certifno) { this.certifno = certifno; } public String getAgentgroupcode() { return agentgroupcode; } public void setAgentgroupcode(String agentgroupcode) { this.agentgroupcode = agentgroupcode; } public String getAgentgroupname() { return agentgroupname; } public void setAgentgroupname(String agentgroupname) { this.agentgroupname = agentgroupname; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
easyexcel的ExcelDataListener 文件 是解析excel時候使用的監聽,在讀取一行數據的時候都要調用invoke()方法,在解析完excel之后執行 doAfterAllAnalysed() 方法,demo中提供了分批次處理的方法
public class ImBnExcelDataListener extends AnalysisEventListener<ImpBnExcelData> { private static final Log log = Log.getLog(ImBnExcelDataListener.class); /** * 每隔5條存儲數據庫,實際使用中可以500條,然后清理list ,方便內存回收 */ private static final int BATCH_COUNT = 500; List<ImpBnExcelData> list = new ArrayList<ImpBnExcelData>(); StringBuilder stringBuilder = new StringBuilder(); @Override public void invoke(ImpBnExcelData data, AnalysisContext context) { System.out.println("解析到一條數據:{}"+JSON.toJSONString(data)); list.add(data); /* list.add(data); if (list.size() >= BATCH_COUNT) { System.out.println("解析到一條數據:{}"+data.getAgentcode()); saveData(list); list.clear(); }else { saveLastData(list); list.clear(); }*/ //saveDataByOne(data); stringBuilder.append("("); //BnController BnController =new BnController(); String convertdata =convertToStr(data); stringBuilder.append(convertdata); stringBuilder.append("),"); } @Override public void doAfterAllAnalysed(AnalysisContext context) { String sqlpro = stringBuilder.toString().substring(0,stringBuilder.toString().lastIndexOf(",")); System.out.println(sqlpro); batchInsert(sqlpro); //saveDataObj(convertToObj(list)); System.out.println("所有數據解析完成!"); } public String convertToStr(ImpBnExcelData data){ String convertdata = "'"+data.getAgentcode()+"','" +data.getName()+"','" +data.getMobile()+"','" +data.getIdno()+"','" +data.getCertifno()+"','" +data.getAgentgroupcode()+"','" +data.getAgentgroupname()+"','" +DateUtil.getTodaySecNum()+"','" +DateUtil.getTodaySec()+"'"; return convertdata; } public static void batchInsert(String sqlpro) { long start = System.currentTimeMillis(); Config config = DbKit.getConfig("datasource"); Connection conn = null; try { conn = config.getConnection(); conn.setAutoCommit(false); StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("insert into table (xx,name,mobile,idno,xx,xx,xx,xx,uptime) values "); stringBuffer.append(sqlpro); PreparedStatement pst = conn.prepareStatement(stringBuffer.toString()); pst.addBatch(); pst.executeBatch(); conn.commit(); pst.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("批量插入需要時間:"+(end - start)); //批量插入需要時間:24675 }
這里我使用了合成 insert value 值的方法,將讀取的excel拼接成一條 insert into 語句,這樣會大大提升批量存儲效率,比一條條存要快的多,如果你的mysql接收sql語句的長度夠,可以寫成一條語句,如果不行就要分批進行存儲,或者修改mysql.ini的 參數,將max_allowed_packet的值改大就行,這里我修改到了16M。 jfinal提供了多數據源多配置的方法可以使用 DbKit.getConfig() 或Db.use("")的方法直接使用數據源或直接調用jdbc,方便的不要不要的。
Controller 文件是jfinal用來做控制轉發的文件,設置好路由之后,直接可以用Controller調用相關方法,在web環境下運行改方法。
public void upbnexcel() { AjaxMsg ajaxMsg = new AjaxMsg(); try { String webrootpath = PathKit.getWebRootPath(); //設置文件上傳子目錄 //String path = "uploads/excel/"; String path =PropKit.get("excel_upload_path"); UploadFile upload = getFile("file", webrootpath + File.separator + path); File file = upload.getFile(); //獲取文件名 String extName = FileUtils.getFileExt(file.getName()); //獲取文件上傳的父目錄 String filePath = upload.getUploadPath(); //時間命名文件 String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + extName; //重命名原來的文件 file.renameTo(new File(filePath + fileName)); long start = System.currentTimeMillis(); EasyExcel.read(filePath+fileName, ExcelData.class, new ExcelDataListener()).sheet().doRead(); log.info("導入耗時s:"+String.valueOf((System.currentTimeMillis()-start)/1000)); ajaxMsg.setState("success"); ajaxMsg.setMsg("上傳成功,耗時"+String.valueOf((System.currentTimeMillis()-start)/1000)+"秒"); } catch (Exception e) { e.printStackTrace(); ajaxMsg.setState("fail"); ajaxMsg.setMsg("上傳失敗:"+e.getMessage()); } renderJson(ajaxMsg); }
通過web方法,先將excel上傳到服務器的upload/excel文件夾下,並通過時間進行命名,然后直接調用 EasyExcel.read方法解析並寫入數據庫,然后通過ajaxMsg返回頁面狀態提示。這里有個注意
UploadFile upload = getFile("file", webrootpath + File.separator + path); 中的 "file" 是cos中的約束,必須要這么寫,否則會上傳失敗
EasyExcel.read 提供了很多讀取的方式,可以選取自己需要的方式進行調整。調整后別忘關閉操作流。
uploadexcel.html頁面引入了layui相關框架,引入的包就不羅列了,直接寫相關內容
<body> <div id="app" class="layui-form"> <div class="container"> <div class="layui-form-item"> <a class="layui-btn layui-btn-warm" href="模板.xlsx" target="_blank">模板下載</a> </div> <blockquote class="layui-elem-quote"> <form class="layui-form" action=""> <div class="layui-form-item"> <div class="layui-inline"> <div class="layui-upload"> <button type="button" class="layui-btn layui-btn-normal" id="file">選擇文件</button> <button type="button" class="layui-btn" id="updo">開始上傳</button> </div> </div> </div> </form> </blockquote> </div> </div> </body>
<script src="../../../static/plugins/layui/layui.js"></script> <script> //一般直接寫在一個js文件中 layui.use(['layer', 'form', 'upload'], function () { var layer = layui.layer , form = layui.form , upload = layui.upload; //選完文件后不自動上傳 var uploadInst = upload.render({ elem: '#file' , url: '/upexcel' , auto: false , accept: 'file' //普通文件 //,multiple: true , bindAction: '#updo' , done: function (res) { //上傳完畢回調 if (res.state == "success") { parent.layer.alert(res.msg); } else { parent.layer.alert(res.msg); return false; } } }); }) }
下載操作比較簡單,注意一下 ajax不能直接下載文件,需要通過 action 或 href 來下載 ,這里我做了兩步請求,先通過選擇列表中需要下載的項,然后執行id序列化操作,然后再次執行查詢和下載操作,才能保存下載的excel文件。
$("#expdata").on('click', function () { var checkStatus = table.checkStatus('tablelist'), data = checkStatus.data; var ids = []; for (var i = 0; i < data.length; i++) { ids.push(data[i].id); } console.log(ids); $.ajax({ //type: 'post', url: '/downExcel', data: {ids: ids}, success: function (response) { console.log(response.idstr); window.location.href="/expExcel?idstr="+response.idstr; }, }); });
/** * 導出excel */ public void expExcel(){ try { //String[] ids = getParaValues("ids[]"); String idstr = getPara("idstr"); HttpServletResponse response=getResponse(); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 這里URLEncoder.encode可以防止中文亂碼 當然和easyexcel沒有關系 String fileName = URLEncoder.encode("測試", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=demo.xlsx"); EasyExcel.write(response.getOutputStream(), ExcelData.class).sheet("模板").doWrite(downExceldata(idstr)); // EasyExcel.write(fileName, ImpBnExcelData.class).sheet("模板").doWrite(data()); }catch (Exception e){ } renderNull(); //renderFile(file,"demo.xlsx"); }
//下載時候首先執行該方法,並將選中的id拆分組合為字符串,再將字符串進行回傳 public void downExcel(){ String[] ids = getParaValues("ids[]"); String idstr = Arrays.toString(ids); idstr = idstr.substring(1, idstr.length() - 1); renderJson("idstr",idstr); } public List<ImpBnExcelData> downExceldata(String idstr){ List<Record> lists =BnService.me().findByIdstr(idstr); List<ImpBnExcelData> bndatalist = new ArrayList<>(); for(Record record : lists) { ImpBnExcelData bnExcelData = new ImpBnExcelData(); ExcelData.setAgentcode(record.getStr("agentcode")); ExcelData.setAgentgroupcode(record.getStr("agentgroupcode")); ExcelData.setAgentgroupname(record.getStr("agentgroupname")); ExcelData.setCertifno(record.getStr("certifno")); ExcelData.setIdno(record.getStr("idno")); ExcelData.setMobile(record.getStr("mobile")); ExcelData.setName(record.getStr("name")); datalist.add(bnExcelData); } return bndatalist; }