今天遇到這樣一個需求,將查查出來的數據導出來,不是將所有的數據導出來,而是要導出滿足條件的數據,也就是說下載的時候要將查詢的條件傳到后台。
例如: 先查詢課程性質是選修的課程然后導出來:
前台封裝條件的form:
<form class="layui-form layui-col-md12 x-so" id="queryCourseForm"> <%--隱藏兩個,一個當前頁,一個頁號--%> <%--當前頁--%> <input type="hidden" name="pageNum"/> <input type="hidden" name="pageSize"/> <div class="layui-input-inline"> <input type="text" name="coursenamecn" placeholder="請輸入課程中文名稱" autocomplete="off" class="layui-input"> </div> <div class="layui-input-inline"> <select name="courseplatform"> <option value="">請選擇課程平台</option> <option value="通識教育">通識教育</option> <option value="學科基礎課">學科基礎課</option> <option value="專業課程">專業課程</option> <option value="個性培養">個性培養</option> <option value="教學環節">教學環節</option> </select> </div> <div class="layui-input-inline"> <select name="coursenature"> <option value="">請選擇課程性質</option> <option value="必修">必修</option> <option value="選修">選修</option> </select> </div> <div class="layui-input-inline"> <select name="credit"> <option value="">請選擇課程學分</option> <option value="0-2">0-2</option> <option value="2-4">2-4</option> <option value="4-1000">4分以上</option> </select> </div> <button class="layui-btn" type="button" onclick="queryCourseFYBtn()"><i class="layui-icon"></i></button> </form>
后台springMCV接收查詢條件並且生成一個excel文件到本地並且提供下載:
package cn.xm.jwxt.controller.trainScheme; import cn.xm.jwxt.service.trainScheme.CourseBaseInfoService; import cn.xm.jwxt.utils.*; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.apache.log4j.Logger; import org.apache.poi.hssf.usermodel.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.lang.reflect.Method; import java.net.URLEncoder; import java.sql.SQLException; import java.util.Date; import java.util.List; import java.util.Map; /** * @Author: qlq * @Description 導出課程信息到Excel中 * @Date: 10:11 2018/4/29 */ @Controller public class ExtCourseExcel { @Autowired private CourseBaseInfoService courseBaseInfoService; private Logger logger = Logger.getLogger(ExtCourseExcel.class); //1.先從緩存中取數據,看能取到取不到 //2.寫入excel到本地 //3.打開流提供下載 //1.查詢數據 public List<Map<String, Object>> getCourseBaseInfosByCondition(@RequestParam Map<String, Object> condition) { List<Map<String, Object>> datas = null; try { datas = courseBaseInfoService.getCourseBaseInfosByCondition(condition); } catch (SQLException e) { logger.error("導出課程信息的時候查詢數據庫出錯",e); } return datas; } //2.寫文件到excel中 /** * 寫數據到本地磁盤 * @param datas 課程數據 * @param fileQualifyName 文件全路徑(比如C:/USER/XXX.excel) */ public void writeCourse2LocalExcel(List<Map<String,Object>> datas,String fileQualifyName){ String[] title = { "序號", "課程編號", "課程平台","課程性質","中文名稱","英文名稱","學分/學時", "周學時分配","計分方式" }; //2.1寫入表頭信息 // 創建一個工作簿 HSSFWorkbook workbook = new HSSFWorkbook(); // 創建一個工作表sheet HSSFSheet sheet = workbook.createSheet(); // 設置列寬 this.setColumnWidth(sheet, 9); // 創建第一行 HSSFRow row = sheet.createRow(0); // 創建一個單元格 HSSFCell cell = null; // 創建表頭 for (int i = 0; i < title.length; i++) { cell = row.createCell(i); // 設置樣式 HSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 設置字體居中 // 設置字體 HSSFFont font = workbook.createFont(); font.setFontName("宋體"); font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);// 字體加粗 // font.setFontHeight((short)12); font.setFontHeightInPoints((short) 13); cellStyle.setFont(font); cell.setCellStyle(cellStyle); cell.setCellValue(title[i]); } // 2.2寫入數據 // 從第二行開始追加數據 for (int i = 1, length_1 = (datas.size() + 1); i < length_1; i++) { // 創建第i行 HSSFRow nextRow = sheet.createRow(i); // 設置樣式 HSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 設置字體居中 // 獲取數據(一條數據) Map<String, Object> course = datas.get(i - 1); for (int j = 0; j < 9; j++) { HSSFCell cell2 = nextRow.createCell(j); cell2.setCellStyle(cellStyle); if (j == 0) { cell2.setCellValue(i);//第一列是序號 continue; } if (j == 1) { cell2.setCellValue(course.get("courseNum").toString());//課程編號 continue; } if (j == 2) { cell2.setCellValue(course.get("coursePlatform").toString());//課程平台 continue; } if (j == 3) { cell2.setCellValue(course.get("courseNature").toString());//課程性質 continue; } if (j == 4) { cell2.setCellValue(course.get("courseNameCN").toString());//中文名稱 continue; } if (j == 5) { cell2.setCellValue(course.get("courseNameEN").toString());//英文名稱 continue; } if (j == 6) { cell2.setCellValue(course.get("credit").toString()+"/"+course.get("courseHour").toString());//學分/學時 continue; } if (j == 7) { cell2.setCellValue(course.get("weeklyHour").toString());//周學時 continue; } if (j == 8) { cell2.setCellValue(course.get("scoringWay").toString());//計分方式 continue; } } } // 創建一個文件 File file = new File(fileQualifyName); // 獲取文件的父文件夾並刪除文件夾下面的文件 File parentFile = file.getParentFile(); // 獲取父文件夾下面的所有文件 File[] listFiles = parentFile.listFiles(); if (parentFile != null && parentFile.isDirectory()) { for (File fi : listFiles) { // 刪除文件 fi.delete(); } } // 如果存在就刪除 if (file.exists()) { file.delete(); } try { file.createNewFile(); // 打開文件流並寫入文件 FileOutputStream outputStream = org.apache.commons.io.FileUtils.openOutputStream(file); workbook.write(outputStream); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 設置列寬的函數 * @param sheet 對哪個sheet進行設置, * @param colNum */ private void setColumnWidth(HSSFSheet sheet, int colNum) { for (int i = 0; i < colNum; i++) { int v = 0; v = Math.round(Float.parseFloat("15.0") * 37F); v = Math.round(Float.parseFloat("20.0") * 267.5F); sheet.setColumnWidth(i, v); } } //3.打開流提供下載 @RequestMapping("/downCourses") public void down(HttpServletRequest request, HttpServletResponse response,@RequestParam Map condition){ //1.查詢數據 List<Map<String, Object>> datas = this.getCourseBaseInfosByCondition(condition); //2.寫入excel String dir = ResourcesUtil.getValue("path","courseExcelFile"); String fileName = DefaultValue.COURSE_DEFAULT_FILENAME; String fileQualifyName = dir + fileName;//生成的excel名字 this.writeCourse2LocalExcel(datas,fileQualifyName);//寫入數據(生成文件) //3.打開流提供下載 //獲取輸入流 try { InputStream bis = new BufferedInputStream(new FileInputStream(new File(fileQualifyName))); fileName = URLEncoder.encode(fileName,"UTF-8"); //設置文件下載頭 response.addHeader("Content-Disposition", "attachment;filename=" + fileName); //1.設置文件ContentType類型,這樣設置,會自動判斷下載文件類型 response.setContentType("multipart/form-data"); BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); int len = 0; while((len = bis.read()) != -1){ out.write(len); out.flush(); } out.close(); } catch (Exception e) { logger.error("下載課程信息出錯!",e); } } }
接下來嘗試用ajax的post提交表單進行下載:
$.ajax({ url:contextPath+"/downCourses.do", type:'post', async:true, data:$("#queryCourseForm").serialize(),//攜帶查詢條件 success:function () { layer.close(index); } });
結果:響應頭正確,數據也正確的傳到后台,但是未下載文件:
總結:
即使ajax請求到一個controller在跳轉到下載的controller上也不能下載,百度了一下總結下原因:發現原來jQuery的ajax回調已經把response的數據傻瓜式的以字符串的方式解析.
解決辦法:
- 第一種:將傳條件的以表單提交的方式進行(推薦這種)-----這種方式也可以用來頁面跳轉
$("#queryCourseForm").attr("action",contextPath+"/downCourses.do");//改變表單的提交地址為下載的地址 $("#queryCourseForm").submit();//提交表單
- 第二種:以window.location.href="xxx"的方式請求下載地址
window.location.href=contextPath+"/downCourses.do"
這種方法需要自己手動的拼接地址傳遞參數。get請求攜帶參數的方式: xxxx.html?username=xxx&password=xxxx
- 第三種:動態創建表單加到fbody中,最后刪除表單(推薦這種,可以將組合條件的值也動態的加入表單中)
//動態創建表單加到fbody中,最后刪除表單 var queryForm = $("#queryCourseForm"); var exportForm = $("<form action='/downCourses.do' method='post'></form>") exportForm.html(queryForm.html()); $(document.body).append(exportForm); exportForm.submit(); exportForm.remove();
注意:動態form必須加到DOM樹,否則會報異常:Form submission canceled because the form is not connected。而且提交完需要刪除元素。
進一步完善代碼:
function download() { try { var queryForm = $("#queryCourseForm"); var exportForm = $("<form action='/downCourses.do' method='post'></form>") exportForm.html(queryForm.html()); $(document.body).append(exportForm); exportForm.submit(); } catch (e) { console.log(e); } finally { exportForm.remove(); } }
補充: 有時候上面查詢條件到不了第二個動態表單中,需要手動添加查詢條件到表單中,如下:
function extTaizhang(){ //動態創建表單加到fbody中,最后刪除表單 var queryForm = $("#queryTaizhangForm"); var exportForm = $("<form action='"+baseurl+"/extSafeHatTaizhang.do' method='post'></form>") exportForm.append("<input type='hidden' name='userName' value='"+$("[name='userName']").val()+"'/>") exportForm.append("<input type='hidden' name='idCard' value='"+$("[name='idCard']").val()+"'/>") exportForm.append("<input type='hidden' name='safeHatNum' value='"+$("[name='safeHatNum']").val()+"'/>") alert(exportForm.serialize()); $(document.body).append(exportForm); exportForm.submit(); exportForm.remove(); }
進一步封裝如下: (第一個參數是form元素的ID,也就是查詢條件所在的form,第二個參數是下載的URL,會自動遍歷條件並拼接查詢條件)
function exportExcel(formId, url) { try { var queryForm = $("#" + formId); var exportForm = $("<form action='" + url + "' method='post'></form>") queryForm.find("input").each(function() { var name = $(this).attr("name"); var value = $(this).val(); exportForm.append("<input type='hidden' name='" + name + "' value='" + value + "'/>") }); queryForm.find("select").each(function() { var name = $(this).attr("name"); var value = $(this).val(); exportForm.append("<input type='hidden' name='" + name + "' value='" + value + "'/>") }); $(document.body).append(exportForm); exportForm.submit(); } catch (e) { console.log(e); } finally { exportForm.remove(); } }
調用代碼:
exportExcel('listPageForm', 'extOperationCharge.do')