POI 導入導出Excel 下拉列表多級聯動


 參考:https://blog.csdn.net/m0_37956938/article/details/78084503

<!-- excel依賴 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>RELEASE</version>
</dependency>

 前端代碼:

<table class="add_table" cellpadding="0" cellspacing="0">
<tr class="main_table_tr">
<td class="td_title" style="width: 25%;"><p class="add_table_p2"> 第一步:</p></td>
<td>
<p style="float: left; margin-left: 10px; font-size: 14px;"> 下載 </p>
<a href="<%=path %>/api/busi/daily/data/downloadTemplate?emId= '${emId}'" style="float: left; margin-left: 11px; font-size: 14px; width: auto;" onclick="downloadExcel(this);"> 日常庫內容模板 </a>
</td>
</tr>
<tr class="main_table_tr">
<td class="td_title" style="width: 25%;"><p class="add_table_p2"> 第二步:</p></td>
<td>
<input type="file" id="file" name="file" style="display: none"/>
<input type="text" id="filename" style="border: none; width: auto; display:none" />
<input type="button" onclick="getExcel()" value="選擇文件" />
</td>
</tr>
</table>
 
        
// 選擇excel並上傳
function getExcel() {
$("#file").click();
$('#file').change(function (e) {
var fileName = e.target.files[0];//js 獲取文件對象
if (fileName !== undefined) {
var file_typename = fileName.name.substring(fileName.name.lastIndexOf('.'));
if (file_typename === '.xlsx' || file_typename === '.xls') {
$("#filename").val(fileName.name);
uploadExcel(fileName);
} else {
layer.alert("請選擇正確的文件類型!");
}
} else {
layer.alert("請選擇正確的文件!");
}
});
}

// 上傳
function uploadExcel(fileName) {
var form = new FormData(); // FormData 對象
form.append("file", fileName); // 文件對象
form.append("dailyId", '${id}');
form.append("emId", '${emId}');
$.ajax({
url: '<%=path %>/api/busi/daily/data/uploadExcel', //url地址
type: 'POST', //上傳方式
data: form, // 上傳formdata封裝的數據
dataType: 'JSON',
cache: false, // 不緩存
processData: false, // jQuery不要去處理發送的數據
contentType: false, // jQuery不要去設置Content-Type請求頭
success:function (data) { //成功回調
if (data.success) {
layer.alert("導入成功", function () {
window.parent.location.reload();
});
} else {
$("#errorTip").find("span").html(data.message);
}
},
error:function (data) { //失敗回調
layer.alert("導入失敗,請稍后重試!");
}
});
}
// 下載Excel模板
function downloadExcel(obj) {
var baseUrl = "具體的下載路徑";
$(obj).attr("href", baseUrl);
$(obj)[0].click();
}

后端代碼:

/**
* 上傳
*/
@RequestMapping(value = "/uploadExcel", method = RequestMethod.POST)
@ResponseBody
public String uploadExcel (@RequestParam("dailyId") String dailyId, @RequestParam("emId") String emId, @RequestParam(value="file", required = false) MultipartFile file) {
boolean flag = false;
try {
List<DailyLibraryDataBean> list = ExcelUtils.readExcel("", DailyLibraryDataBean.class, file);
String msg = "";
for (int i=1;i<=list.size();i++) {
DailyLibraryDataBean data = list.get(i-1);
data.setDailyId(dailyId);
String ms = "";
DevicePartBean bean = new DevicePartBean();
if (!"".equals(data.getPartId()) && data.getPartId() != null) {

bean = devicePartBiz.fetch(Cnd.where("part_name", "=", data.getPartId()).and("parent_id", "is", null).and("em_id", "=", emId));

if (bean != null) {
data.setPartId(bean.getId());

if (!"".equals(data.getPartId2()) && data.getPartId2() != null) {
DevicePartBean bean2 = devicePartBiz.fetch(Cnd.where("part_name", "=", data.getPartId2()).and("parent_id", "=", bean.getId()).and("em_id", "=", emId));
if (bean2 != null) {
data.setPartId2(bean2.getId());
} else {
ms += "第" + i + "行設備型號查無此二級部件;";
}
} else {
data.setPartId2("");
}
} else {
ms += "第" + i + "行設備型號查無此一級部件;";
}
} else {
data.setPartId("");
}

if ("".equals(data.getDailyContent()) || data.getDailyContent() == null) {
ms += "第" + i + "行作業內容不能為空;";
}
if ("".equals(data.getDailySop()) || data.getDailySop() == null) {
ms += "第" + i + "行作業SOP不能為空;";
}
if ("".equals(data.getDailyTool()) || data.getDailyTool() == null) {
ms += "第" + i + "行作業工具方法不能為空;";
}
if ("".equals(data.getCycle()) || data.getCycle() == null) {
ms += "第" + i + "行作業周期不能為空;";
}
if ("".equals(data.getCycleUnit()) || data.getCycleUnit() == null) {
ms += "第" + i + "行周期單位不能為空;";
} else {
switch (data.getCycleUnit()) {
case "年" : data.setCycleUnit("cycle_unit_year");
break;

case "月" : data.setCycleUnit("cycle_unit_month");
break;

case "周" : data.setCycleUnit("cycle_unit_week");
break;

case "日" : data.setCycleUnit("cycle_unit_day");
break;

default: ms += "第" + i + "行查無此周期單位;";
break;
}
}
if ("".equals(data.getExcepResultType()) || data.getExcepResultType() == null) {
ms += "第" + i + "行作業異常返回類型不能為空;";
} else {
switch (data.getExcepResultType()) {
case "預警" : data.setExcepResultType("excep_result_alarm");
break;

default: ms += "第" + i + "行查無此作業異常返回類型;";
break;
}
}
if ("".equals(data.getExcepSchemes()) || data.getExcepSchemes() == null) {
ms += "第" + i + "行異常處理方案不能為空;";
}
if (StringUtils.isNotBlank(ms)) {
ms += "\r\n";
msg += ms;
continue;
}
dataBiz.insert(data);
}
if (StringUtils.isNotBlank(msg)) {
return Json.toJson(new ApiResult(msg));
}
flag = true;
} catch (Exception e) {
e.printStackTrace();
}

return Json.toJson(new ApiResult(flag, flag == true));
}

/**
* 下載 Excel模板
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/downloadTemplate")
public void downloadTemplate(String emId, HttpServletRequest request, HttpServletResponse response) throws IOException {
Workbook workbook = new XSSFWorkbook();
String fileName = "template_daily_library"+ ".xlsx"; // Excel文件名
OutputStream os = null;
workbook = buildExcelDocument(emId);
response.reset();// 清空輸出流
// web瀏覽通過MIME類型判斷文件是excel類型
response.setHeader("Content-type",
"application/vnd.ms-excel;charset=UTF-8");

// 對文件名進行處理。防止文件名亂碼
// Content-disposition屬性設置成以附件方式進行下載
response.addHeader("Content-Disposition", "attachment;filename="
+ new String(fileName.getBytes("gb2312"), "ISO8859-1"));
os = response.getOutputStream();// 取得輸出流
workbook.write(os);
}

/**
* 創建excel
* @param emId
* @return
*/
public XSSFWorkbook buildExcelDocument(String emId) {
// 創建一個excel
XSSFWorkbook workbook = new XSSFWorkbook();
// 樣式一
XSSFFont font1 = workbook.createFont();
font1.setFontHeightInPoints((short) 12); // 設置字體的大小
font1.setFontName("宋體"); // 設置字體的樣式,如:宋體、微軟雅黑等
font1.setItalic(false); // 斜體true為斜體

// 樣式二
XSSFFont font2 = workbook.createFont();
font2.setFontHeightInPoints((short) 12); // 設置字體的大小
font2.setFontName("宋體"); // 設置字體的樣式,如:宋體、微軟雅黑等
font2.setColor(Font.COLOR_RED); // 紅色顯示必填
font2.setItalic(false); // 斜體true為斜體

XSSFCellStyle style1 = workbook.createCellStyle(); // 獲取樣式1
XSSFCellStyle style2 = workbook.createCellStyle(); // 獲取樣式2

XSSFSheet sheet1 = workbook.createSheet("sheet1");
Row row0 = sheet1.createRow(0);

String[] headers = new String[] { "一級部件", "二級部件", "作業內容", "作業SOP", "作業工具方法", "作業周期", "周期單位", "作業異常返回類型", "異常處理方案" };
for (int i = 0; i < headers.length; i++) {
sheet1.setColumnWidth(i, 5000); //設置每列的列寬
Cell cell = row0.createCell(i, Cell.CELL_TYPE_STRING);
if (i==0 || i==1) {
style1.setFont(font1);// 選擇需要用到的字體格式
cell.setCellStyle(style1);
} else {
style2.setFont(font2);// 選擇需要用到的字體格式
cell.setCellStyle(style2);
}

cell.setCellValue(headers[i]);
}


// 查詢所有的一級部件二級部件名稱
List<String> part1List = new ArrayList<String>(); // 一級部件列表
part1List.add("");
List<String> strList6 = new ArrayList<String>(); // 周期單位列表
strList6.add("年");
strList6.add("月");
strList6.add("周");
strList6.add("日");

List<String> strList7 = new ArrayList<String>(); // 作業異常返回類型列表
strList7.add("預警");

// 整理數據,放入一個Map中,mapkey存放父地點,value 存放該地點下的子區域
Map<String, List<String>> map = new HashMap<String, List<String>>();

// 一級部件
Map<String, Object> params1 = new HashMap<String, Object>();
Query query1 = new Query(params1);
query1.put("deleteFlag", DelStatus.status_Y); // 默認查詢未刪除
query1.put("parentId", "is null");
query1.put("asc", "crtTime");
query1.put("emId", emId);
List<DevicePartBean> parts1 = devicePartBiz.selectByQuery(query1); // 一級部件

// 二級部件
Map<String, Object> params2 = new HashMap<String, Object>();
Query query2 = new Query(params2);
query2.put("deleteFlag", DelStatus.status_Y); // 默認查詢未刪除

for (DevicePartBean p1 : parts1) {
String partId1 = p1.getId();
String partName1 = p1.getPartName();
part1List.add(partName1);
query2.put("emId", emId);
query2.put("parentId", partId1);
query2.put("asc", "crtTime");
List<DevicePartBean> parts2 = devicePartBiz.selectByQuery(query2); // 二級部件
List<String> part2List = new ArrayList<String>(); // 二級部件列表
if (parts2.size()>0) {
for (DevicePartBean part2 : parts2) {
String partName2 = part2.getPartName();
part2List.add(partName2);
}
} else {
part2List.add("");
}
map.put(partName1, part2List);
}

// 創建一個專門用來存放一級部件二級部件的隱藏sheet頁
Sheet hideSheet = workbook.createSheet("site_sheet");
workbook.setSheetHidden(workbook.getSheetIndex("site_sheet"), true);

int rowId = 0;
// 設置第一行,存一級部件的信息
Row part1Row = hideSheet.createRow(rowId++);
part1Row.createCell(0).setCellValue("一級部件列表");
for (int i = 0; i < part1List.size(); i++) {
Cell part1Cell = part1Row.createCell(i + 1);
part1Cell.setCellValue(part1List.get(i));
}
// 將具體的數據寫入到每一行中,行開頭為父級區域,后面是子區域
Iterator<String> keyIterator = map.keySet().iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
List<String> son = map.get(key);

Row row = hideSheet.createRow(rowId++);
row.createCell(0).setCellValue(key);
for (int i = 0; i < son.size(); i++) {
Cell cell = row.createCell(i + 1);
cell.setCellValue(son.get(i));
}

// 添加名稱管理器
String range = getRange(1, rowId, son.size());
Name name = workbook.createName();
name.setNameName(key);
String formula = "site_sheet!" + range;
name.setRefersToFormula(formula);
}

XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper(sheet1);
// 一級部件規則
DataValidationConstraint partConstraint = dvHelper.createExplicitListConstraint(part1List.toArray(new String[] {}));
// 四個參數分別是:起始行、終止行、起始列、終止列
CellRangeAddressList partRangeAddressList = new CellRangeAddressList(1, 200, 0, 0);
DataValidation partDataValidation = dvHelper.createValidation(partConstraint, partRangeAddressList);
//驗證
partDataValidation.createErrorBox("error", "請選擇正確的一級部件");
partDataValidation.setShowErrorBox(true);
partDataValidation.setSuppressDropDownArrow(true);
sheet1.addValidationData(partDataValidation);

// 周期單位規則
DataValidationConstraint cycleUnitConstraint = dvHelper.createExplicitListConstraint(strList6.toArray(new String[] {}));
// 四個參數分別是:起始行、終止行、起始列、終止列
CellRangeAddressList cycleUnitRangeAddressList = new CellRangeAddressList(1, 200, 6, 6);
DataValidation cycleUnitDataValidation = dvHelper.createValidation(cycleUnitConstraint, cycleUnitRangeAddressList);
//驗證
cycleUnitDataValidation.createErrorBox("error", "請選擇正確的周期單位");
cycleUnitDataValidation.setShowErrorBox(true);
cycleUnitDataValidation.setSuppressDropDownArrow(true);
sheet1.addValidationData(cycleUnitDataValidation);

// 作業異常返回類型規則
DataValidationConstraint exceptConstraint = dvHelper.createExplicitListConstraint(strList7.toArray(new String[] {}));
// 四個參數分別是:起始行、終止行、起始列、終止列
CellRangeAddressList exceptRangeAddressList = new CellRangeAddressList(1, 200, 7, 7);
DataValidation exceptDataValidation = dvHelper.createValidation(exceptConstraint, exceptRangeAddressList);
//驗證
exceptDataValidation.createErrorBox("error", "請選擇正確的作業異常返回類型");
exceptDataValidation.setShowErrorBox(true);
exceptDataValidation.setSuppressDropDownArrow(true);
sheet1.addValidationData(exceptDataValidation);


//對前20行設置有效性
for(int i = 2;i < 200;i++){
setDataValidation("A" ,sheet1,i,2);
}

return workbook;
}

/**
* 設置有效性
* @param offset 主影響單元格所在列,即此單元格由哪個單元格影響聯動
* @param sheet
* @param rowNum 行數
* @param colNum 列數
*/
public static void setDataValidation(String offset,XSSFSheet sheet, int rowNum,int colNum) {
XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper(sheet);
DataValidation data_validation_list;
data_validation_list = getDataValidationByFormula(
"INDIRECT($" + offset + (rowNum) + ")", rowNum, colNum,dvHelper);
sheet.addValidationData(data_validation_list);
}

/**
* 加載下拉列表內容
* @param formulaString
* @param naturalRowIndex
* @param naturalColumnIndex
* @param dvHelper
* @return
*/
private static DataValidation getDataValidationByFormula(
String formulaString, int naturalRowIndex, int naturalColumnIndex,XSSFDataValidationHelper dvHelper) {
// 加載下拉列表內容
// 舉例:若formulaString = "INDIRECT($A$2)" 表示規則數據會從名稱管理器中獲取key與單元格 A2 值相同的數據,
XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint) dvHelper.createFormulaListConstraint(formulaString);
// 設置數據有效性加載在哪個單元格上。
// 四個參數分別是:起始行、終止行、起始列、終止列
int firstRow = naturalRowIndex -1;
int lastRow = naturalRowIndex - 1;
int firstCol = naturalColumnIndex - 1;
int lastCol = naturalColumnIndex - 1;
CellRangeAddressList regions = new CellRangeAddressList(firstRow,
lastRow, firstCol, lastCol);
// 數據有效性對象
// 綁定
XSSFDataValidation data_validation_list = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
data_validation_list.setEmptyCellAllowed(false);
if (data_validation_list instanceof XSSFDataValidation) {
data_validation_list.setSuppressDropDownArrow(true);
data_validation_list.setShowErrorBox(true);
} else {
data_validation_list.setSuppressDropDownArrow(false);
}
// 設置輸入信息提示信息
data_validation_list.createPromptBox("下拉選擇提示", "請使用下拉方式選擇合適的值!");
// 設置輸入錯誤提示信息
//data_validation_list.createErrorBox("選擇錯誤提示", "你輸入的值未在備選列表中,請下拉選擇合適的值!");
return data_validation_list;
}

/**
*
* @param offset 偏移量,如果給0,表示從A列開始,1,就是從B列
* @param rowId 第幾行
* @param colCount 一共多少列
* @return 如果給入參 1,1,10. 表示從B1-K1。最終返回 $B$1:$K$1
*
* @author wb 2020年1月04日 15:00:00
*/
public static String getRange(int offset, int rowId, int colCount) {
char start = (char)('A' + offset);
if (colCount <= 25) {
char end = (char)(start + colCount - 1);
return "$" + start + "$" + rowId + ":$" + end + "$" + rowId;
} else {
char endPrefix = 'A';
char endSuffix = 'A';
if ((colCount - 25) / 26 == 0 || colCount == 51) {// 26-51之間,包括邊界(僅兩次字母表計算)
if ((colCount - 25) % 26 == 0) {// 邊界值
endSuffix = (char)('A' + 25);
} else {
endSuffix = (char)('A' + (colCount - 25) % 26 - 1);
}
} else {// 51以上
if ((colCount - 25) % 26 == 0) {
endSuffix = (char)('A' + 25);
endPrefix = (char)(endPrefix + (colCount - 25) / 26 - 1);
} else {
endSuffix = (char)('A' + (colCount - 25) % 26 - 1);
endPrefix = (char)(endPrefix + (colCount - 25) / 26);
}
}
return "$" + start + "$" + rowId + ":$" + endPrefix + endSuffix + "$" + rowId;
}
}
 
         
         
        
public class ExcelUtils {

private final static Logger log = LoggerFactory.getLogger(ExcelUtils.class);

private final static String EXCEL2003 = "xls";
private final static String EXCEL2007 = "xlsx";

public static <T> List<T> readExcel(String path, Class<T> cls,MultipartFile file){

String fileName = file.getOriginalFilename();
if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
log.error("上傳文件格式不正確");
}
List<T> dataList = new ArrayList<>();
Workbook workbook = null;
try {
InputStream is = file.getInputStream();
if (fileName.endsWith(EXCEL2007)) {
// FileInputStream is = new FileInputStream(new File(path));
workbook = new XSSFWorkbook(is);
}
if (fileName.endsWith(EXCEL2003)) {
// FileInputStream is = new FileInputStream(new File(path));
workbook = new HSSFWorkbook(is);
}
if (workbook != null) {
//類映射 注解 value-->bean columns
Map<String, List<Field>> classMap = new HashMap<>();
List<Field> fields = Stream.of(cls.getDeclaredFields()).collect(Collectors.toList());
fields.forEach(
field -> {
ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
if (annotation != null) {
String value = annotation.value();
if (StringUtils.isBlank(value)) {
return;//return起到的作用和continue是相同的 語法
}
if (!classMap.containsKey(value)) {
classMap.put(value, new ArrayList<>());
}
field.setAccessible(true);
classMap.get(value).add(field);
}
}
);
//索引-->columns
Map<Integer, List<Field>> reflectionMap = new HashMap<>(16);
//默認讀取第一個sheet
Sheet sheet = workbook.getSheetAt(0);

boolean firstRow = true;
for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
//首行 提取注解
if (firstRow) {
for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) {
Cell cell = row.getCell(j);
String cellValue = getCellValue(cell);
if (classMap.containsKey(cellValue)) {
reflectionMap.put(j, classMap.get(cellValue));
}
}
firstRow = false;
} else {
//忽略空白行
if (row == null) {
continue;
}
try {
T t = cls.newInstance();
//判斷是否為空白行
boolean allBlank = true;
for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) {
if (reflectionMap.containsKey(j)) {
Cell cell = row.getCell(j);
String cellValue = getCellValue(cell);
if (StringUtils.isNotBlank(cellValue)) {
allBlank = false;
}
List<Field> fieldList = reflectionMap.get(j);
fieldList.forEach(
x -> {
try {
handleField(t, cellValue, x);
} catch (Exception e) {
log.error(String.format("reflect field:%s value:%s exception!", x.getName(), cellValue), e);
}
}
);
}
}
if (!allBlank) {
dataList.add(t);
} else {
log.warn(String.format("row:%s is blank ignore!", i));
}
} catch (Exception e) {
log.error(String.format("parse row:%s exception!", i), e);
}
}
}
}
} catch (Exception e) {
log.error(String.format("parse excel exception!"), e);
} finally {
if (workbook != null) {
try {
// workbook.close();
} catch (Exception e) {
log.error(String.format("parse excel exception!"), e);
}
}
}
return dataList;
}

private static <T> void handleField(T t, String value, Field field) throws Exception {
Class<?> type = field.getType();
if (type == null || type == void.class || StringUtils.isBlank(value)) {
return;
}
if (type == Object.class) {
field.set(t, value);
//數字類型
} else if (type.getSuperclass() == null || type.getSuperclass() == Number.class) {
if (type == int.class || type == Integer.class) {
field.set(t, NumberUtils.toInt(value));
} else if (type == long.class || type == Long.class) {
field.set(t, NumberUtils.toLong(value));
} else if (type == byte.class || type == Byte.class) {
field.set(t, NumberUtils.toByte(value));
} else if (type == short.class || type == Short.class) {
field.set(t, NumberUtils.toShort(value));
} else if (type == double.class || type == Double.class) {
field.set(t, NumberUtils.toDouble(value));
} else if (type == float.class || type == Float.class) {
field.set(t, NumberUtils.toFloat(value));
} else if (type == char.class || type == Character.class) {
field.set(t, CharUtils.toChar(value));
} else if (type == boolean.class) {
field.set(t, BooleanUtils.toBoolean(value));
} else if (type == BigDecimal.class) {
field.set(t, new BigDecimal(value));
}
} else if (type == Boolean.class) {
field.set(t, BooleanUtils.toBoolean(value));
} else if (type == Date.class) {
//
field.set(t, value);
} else if (type == String.class) {
field.set(t, value);
} else {
Constructor<?> constructor = type.getConstructor(String.class);
field.set(t, constructor.newInstance(value));
}
}

private static String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
if (HSSFDateUtil.isCellDateFormatted(cell)) {
return HSSFDateUtil.getJavaDate(cell.getNumericCellValue()).toString();
} else {
return new BigDecimal(cell.getNumericCellValue()).toString();
}
} else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
return StringUtils.trimToEmpty(cell.getStringCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
return StringUtils.trimToEmpty(cell.getCellFormula());
} else if (cell.getCellType() == Cell.CELL_TYPE_BLANK) {
return "";
} else if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
return String.valueOf(cell.getBooleanCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_ERROR) {
return "ERROR";
} else {
return cell.toString().trim();
}

}
}
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM