엑셀 파일을 업로드해서 해당 파일 안의 데이터를 데이터베이스에 넣는 기능을 구현했다.
poi 라이브러리를 활용했으며 엑셀 업로드 기능을 구현한 소스를 공유하고자 한다. 구글링해서 나온 소스와 일부 내용을 수정하여 정리했다.
1. pom.xml에 Dependency 추가하기
<!-- Excel 파일 읽고 쓰기를 위한 Dependency 추가(poi 라이브러리) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.11</version>
</dependency>
2. ExcelFileType.java : 엑셀 파일 확장자 비교 -> Workbook 객체에 초기화
package com.ctit.helper;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.sl.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/*
* ExcelFileType.java
* Excel 파일을 읽어 확장자를 비교하는 java 파일
*/
//엑셀 파일을 읽어서 Workbook 객체에 리턴한다.
//.xls와 .xlsx 확장자를 비교한다.
public class ExcelFileType{
/*
* FileInputStream은 파일의 경로에 있는 파일을 읽어서 Byte로 가져온다.
* 파일이 존재하지 않는다면 RuntimeException 발생
*/
public static Workbook getWorkbook(String filePath) {
FileInputStream fis = null;
try {
fis=new FileInputStream(filePath);
} catch(FileNotFoundException e) {
throw new RuntimeException(e.getMessage(), e);
}
/*
* 파일의 확장자를 체크해서 .XLS라면 HSSFWorkbook에
* .XLSX라면 XSSFWorkbook에 각각 초기화한다.
*/
Workbook wb=null;
if(filePath.toUpperCase().endsWith(".XLS")) {
try {
wb=new HSSFWorkbook(fis);
} catch(IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
else if(filePath.toUpperCase().endsWith(".XLSX")) {
try {
wb=new XSSFWorkbook(fis);
} catch(IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
if(fis!=null) {
try {
fis.close();
} catch(IOException e) {
System.out.println("예외 상황 발생");
}
}
return wb;
}
}
3. ExcelReadOption.java : 엑셀 파일 읽을 때 옵션 설정
- filePath : 엑셀 파일 경로
- outputColumns : 추출할 컬럼명
- startRow : 추출 시작할 행번호
- sheetNum : 시트 번호
- getter, setter
package com.ctit.helper;
import java.util.ArrayList;
import java.util.List;
//엑셀 파일을 읽을 때 옵션을 설정하는 java 파일
public class ExcelReadOption {
private String filePath;//엑셀 파일의 경로
private List<String> outputColumns;//추출할 컬럼명
private int startRow;//추출을 시작할 행 번호
private int sheetNum;
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath=filePath;
}
public List<String> getOutputColumns(){
List<String> temp=new ArrayList<String>();
temp.addAll(outputColumns);
return temp;
}
public void setOutputColumns(List<String> outputColumns) {
List<String> temp=new ArrayList<String>();
temp.addAll(outputColumns);
this.outputColumns=temp;
}
public void setOutputColumns(String ... outputColumns) {
if(this.outputColumns == null) {
this.outputColumns=new ArrayList<String>();
}
for(String outputColumn : outputColumns) {
this.outputColumns.add(outputColumn);
}
}
public int getStartRow() {
return startRow;
}
public void setStartRow(int startRow) {
this.startRow=startRow;
}
public int getSheetNum() {
return sheetNum;
}
public void setSheetNum(int sheetNum) {
this.sheetNum=sheetNum;
}
}
4. ExcelCellRef.java : 해당 Cell의 컬럼 이름, 값 가져오기
package com.ctit.helper;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.util.CellReference;
// Cell의 이름과 값을 가져오는 java 파일
public class ExcelCellRef {
/*
* Cell의 이름(A, B, C, ..)을 가져오는 메소드
* 매개변수로 전달된 Cell 객체가 널이 아니면 : 해당 셀의 열 인덱스를 가져온다.
* Cell 객체가 널인 경우 : 메소드에 전달된 cellIndex를 사용
* 열 인덱스를 열 이름으로 변환하여 반환
*/
public static String getName(Cell cell, int cellIndex) {
int celNum=0;
if(cell!=null) {
celNum=cell.getColumnIndex();
}else {
celNum=cellIndex;
}
return CellReference.convertNumToColString(celNum);
}
/*
* Cell의 값을 문자열 형태로 가져오는 메소드
* 매개변수로 전달된 Cell 객체가 널이면 : 빈 문자열 반환
* Cell 객체가 널이 아니면 : 셀의 데이터 타입(cellType)에 따라 적절한 값 읽어서 문자열로 반환
*/
public static String getValue(Cell cell) {
String value="";
if(cell==null) {
value="";
}else {
if(cell.getCellType() == Cell.CELL_TYPE_FORMULA) { //수식
value=cell.getCellFormula();
}
else if(cell.getCellType()==Cell.CELL_TYPE_NUMERIC) { //숫자
value=cell.getNumericCellValue()+"";
}
else if(cell.getCellType() == Cell.CELL_TYPE_STRING) { //문자열
value=cell.getStringCellValue();
}
else if(cell.getCellType()==Cell.CELL_TYPE_BOOLEAN) { //불리언
value=cell.getBooleanCellValue()+"";
}
else if(cell.getCellType()==Cell.CELL_TYPE_ERROR) { //에러
value=cell.getErrorCellValue()+"";
}
else if(cell.getCellType()==Cell.CELL_TYPE_BLANK) { //공백
value="";
}
else { // 기타 경우에도 문자열 값 직접 가져온다.
value=cell.getStringCellValue();
}
}
return value;
}
}
5. ExcelRead.java : Excel 파일을 읽어온다.
<로직 정리>
package com.ctit.helper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; // XSSFWorkbook을 사용하는 경우
// Excel 파일을 읽어오는 java 파일
public class ExcelRead {
public static List<Map<String, String>> read(ExcelReadOption excelReadOption) throws InvalidFormatException, IOException{
//Workbook : Apache POI 라이브러리에서 Excel 파일 전체를 나타내는 객체
//해당 경로에 해당하는 Excel 파일 로드하여 Workbook 객체 생성
Workbook wb = ExcelFileType.getWorkbook(excelReadOption.getFilePath());
System.out.println("Sheet 이름 : "+wb.getSheetName(0));
System.out.println("데이터가 있는 Sheet의 수 : "+wb.getNumberOfSheets());
int sheetNum=wb.getNumberOfSheets();
Row row=null; //Sheet내의 행. 접근법 : sheet.getRow(rowIndex)
Cell cell=null; //Sheet내의 셀. 접근법 : row.getCell(cellIndex)
String cellName="";//해당 셀의 헤더(컬럼명)
int numOfCells=0;
Map<String, String> paramMap=null; //추출된 데이터를 저장하는 Map객체
List<Map<String, String>> result=new ArrayList<Map<String, String>>();
for(int sheetIndex=0; sheetIndex<wb.getNumberOfSheets(); sheetIndex++)
{
//해당 sheetIndex에 해당하는 특정 개별 시트 가져온다.
Sheet sheet= wb.getSheetAt(sheetIndex);
//각 Row만큼 반복한다.
for(int rowIndex = excelReadOption.getStartRow() - 1; rowIndex < sheet.getLastRowNum() + 1; rowIndex++)
{
row = sheet.getRow(rowIndex);
Row HeaderRow = sheet.getRow(0);
if(row!=null) {
numOfCells = row.getLastCellNum();
paramMap = new HashMap<String, String>();//데이터를 담을 맵 객체 초기화
for(int cellIndex = 0; cellIndex < numOfCells; cellIndex++) {
//Row에서 CellIndex에 해당하는 Cell을 가져온다.
cell = row.getCell(cellIndex);
//현재 Cell의 Header 이름을 가져온다.
cellName = ExcelCellRef.getValue(HeaderRow.getCell(cellIndex));
//추출 대상 컬럼인지 확인. 추출대상컬럼 아니면 for로 다시 올라간다.
if(!excelReadOption.getOutputColumns().contains(cellName)) {
continue;
}//end of if문
paramMap.put(cellName, ExcelCellRef.getValue(cell));
}//end of for문
result.add(paramMap);
}//end of if
}//end of for
}//end of for
return result;
}//end of method read()
}//end of class ExcelRead
6. 엑셀 업로드 폼
나는 [엑셀 업로드] 버튼을 클릭하면 모달 창이 떠서 아래와 같은 form이 뜨도록 개발했다.
7. 엑셀 업로드 폼에서 저장 버튼 누르면 실행되는 Javascript
아래의 check() 함수는 사용자가 웹 페이지에서 파일을 업로드 할 때 사용된다. Excel 파일을 서버로 비동기적으로 업로드하기 위한 로직을 포함한다. 즉, 사용자로부터 파일 업로드를 확인받고 선택된 파일을 서버로 전송한다.
여기서 잠깐, var formData = new FormData(); 에서 사용한 FormData 객체란 무엇일까?
'FormData' 객체는 자바스크립트에서 HTML 폼(form) 데이터를 쉽게 생성하고 서버로 전송할 수 있게 해주는 Web API이다. 이 객체를 사용하면 AJAX요청을 통해 데이터를 비동기적으로 서버에 전송할 때 폼에 입력된 데이터를 쉽게 처리할 수 있다. 파일이나 텍스트 데이터 같은 복잡한 데이터 타입도 처리할 수 있어, 파일 업로드 기능 구현 시 유용하다.
[ 기본 사용법 ]
1. 기본 객체 생성 방법
var formData = new FormData();
2. 이후 append 메소드를 통해 키-값 쌍을 추가 가능
formData.append('key', 'value')
3. HTML <form>요소의 데이터 기반으로 'FormData'객체 생성하기
예) form 요소에 id="myForm"이라는 속성이 있는 경우의 'FormData'객체 초기화
var formElement = document.getElementById("myForm");
var formData = new FormData(formElement);
4. 파일 데이터 추가하기
'formData' 객체는 파일 업로드 시 유용하다.
<input type="file">을 통해 선택된 파일을 쉽게 추가할 수 있다.
var fileInput = document.querySelector('input[type="file"]');
formData.append('file', fileInput.files[0]);
5. AJAX를 통해 서버로 전송하기
'FormData' 객체를 'XMLHttpRequest' 또는 'fetch API'와 함께 사용하여 서버로 데이터를
비동기적으로 전송가능하다.
(예시 1) XMLHttpRequest 사용
var xhr = new XMLHttpRequest();
xhr.open('POST', '/target-url');
xhr.send(formData);
(예시 2) fetch API 사용
fetch('/target-url', {
method: 'POST',
body: formData
});
FormData 객체 사용 시 장점
- 멀티파트(multipart/form-data) 인코딩 타입을 사용하여 파일과 같은 큰 데이터를 처리할 수 있다.
- 폼 데이터를 쉽게 조작, 추가 가능하다.
- 자바스크립트에서 직접 폼 데이터를 조작하고 서버로 전송가능 => 폼 전송 시 페이지 새로고침 안하고 비동기적으로 서버에 데이터 전송가능
8. Controller 단
9. Service.java
package def.admmgr.manage;
import java.io.File;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Service {
public void excelUpload(File destFile) throws Exception;
public String excelDate(File destFile) throws Exception;
}
10. ServiceImpl.java
11. xml
참고 블로그
https://daydreamer-92.tistory.com/42
https://take-it-into-account.tistory.com/175
'자바(Java)' 카테고리의 다른 글
캡슐화, 상속, 다형성 그리고 OCP 원칙 (1) | 2024.04.11 |
---|