|
|
|
|
@ -4,9 +4,8 @@ import org.apache.poi.poifs.crypt.EncryptionInfo;
|
|
|
|
|
import org.apache.poi.poifs.crypt.EncryptionMode;
|
|
|
|
|
import org.apache.poi.poifs.crypt.Encryptor;
|
|
|
|
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
|
|
|
import org.apache.poi.ss.usermodel.CellStyle;
|
|
|
|
|
import org.apache.poi.ss.usermodel.Row;
|
|
|
|
|
import org.apache.poi.ss.usermodel.Sheet;
|
|
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
|
|
|
|
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
@ -23,25 +22,91 @@ public abstract class BaseSxssfExcelFile implements ExcelFile {
|
|
|
|
|
protected static final int COLUMN_START_INDEX = 0;
|
|
|
|
|
protected SXSSFWorkbook workbook;
|
|
|
|
|
protected Sheet sheet;
|
|
|
|
|
protected int titleRowOffset = 0; // 제목 행이 있으면 1, 없으면 0
|
|
|
|
|
|
|
|
|
|
public BaseSxssfExcelFile() {
|
|
|
|
|
this.workbook = new SXSSFWorkbook(ROW_ACCESS_WINDOW_SIZE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 제목 행을 렌더링합니다.
|
|
|
|
|
* 첫 번째 행에 제목을 생성하고 모든 열에 걸쳐 셀을 병합합니다.
|
|
|
|
|
*
|
|
|
|
|
* @param sheetName 시트명
|
|
|
|
|
* @param title 제목 텍스트
|
|
|
|
|
* @param columnCount 열 개수
|
|
|
|
|
*/
|
|
|
|
|
protected void renderTitleRow(String sheetName, String title, int columnCount) {
|
|
|
|
|
// 시트가 없으면 생성
|
|
|
|
|
if (sheet == null) {
|
|
|
|
|
sheet = workbook.createSheet(sheetName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 제목 행 생성 (row 0)
|
|
|
|
|
Row titleRow = sheet.createRow(ROW_START_INDEX);
|
|
|
|
|
|
|
|
|
|
// 제목 스타일 생성 (굵게, 가운데 정렬)
|
|
|
|
|
CellStyle titleStyle = workbook.createCellStyle();
|
|
|
|
|
titleStyle.setAlignment(HorizontalAlignment.LEFT);
|
|
|
|
|
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
|
|
|
|
|
|
|
// 제목 폰트 생성 (맑은 고딕, 22 포인트, 굵게)
|
|
|
|
|
Font titleFont = workbook.createFont();
|
|
|
|
|
//titleFont.setFontName("맑은 고딕"); 폰트 개이상함
|
|
|
|
|
titleFont.setFontHeightInPoints((short) 22);
|
|
|
|
|
titleFont.setBold(true);
|
|
|
|
|
titleStyle.setFont(titleFont);
|
|
|
|
|
|
|
|
|
|
// 제목 행 높이 설정
|
|
|
|
|
titleRow.setHeightInPoints(33);
|
|
|
|
|
|
|
|
|
|
// 첫 번째 셀에 제목 입력
|
|
|
|
|
createCell(titleRow, COLUMN_START_INDEX, title, titleStyle);
|
|
|
|
|
|
|
|
|
|
// 모든 열에 걸쳐 셀 병합 (row 0, column 0 ~ columnCount-1)
|
|
|
|
|
if (columnCount > 1) {
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(
|
|
|
|
|
ROW_START_INDEX, // 시작 행
|
|
|
|
|
ROW_START_INDEX, // 종료 행
|
|
|
|
|
COLUMN_START_INDEX, // 시작 열
|
|
|
|
|
columnCount - 1 // 종료 열
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 제목 행 오프셋 설정
|
|
|
|
|
titleRowOffset = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void renderHeaders(ExcelMetadata excelMetadata) {
|
|
|
|
|
sheet = workbook.createSheet(excelMetadata.getSheetName());
|
|
|
|
|
Row row = sheet.createRow(ROW_START_INDEX);
|
|
|
|
|
// 시트가 없으면 생성 (제목 행이 없는 경우)
|
|
|
|
|
if (sheet == null) {
|
|
|
|
|
sheet = workbook.createSheet(excelMetadata.getSheetName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 헤더 행 생성 (제목 행이 있으면 row 1, 없으면 row 0)
|
|
|
|
|
Row row = sheet.createRow(ROW_START_INDEX + titleRowOffset);
|
|
|
|
|
int columnIndex = COLUMN_START_INDEX;
|
|
|
|
|
CellStyle style = createCellStyle(workbook, true);
|
|
|
|
|
style.setAlignment(HorizontalAlignment.CENTER);
|
|
|
|
|
for (String fieldName : excelMetadata.getDataFieldNames()) {
|
|
|
|
|
createCell(row, columnIndex++, excelMetadata.getHeaderName(fieldName), style);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SXSSFWorkbook에서 autoSizeColumn을 사용하기 위해 각 열을 추적하도록 설정
|
|
|
|
|
// 헤더 생성 직후 추적을 시작하여 헤더도 너비 계산에 포함되도록 함
|
|
|
|
|
int columnCount = excelMetadata.getDataFieldNames().size();
|
|
|
|
|
for (int i = 0; i < columnCount; i++) {
|
|
|
|
|
((org.apache.poi.xssf.streaming.SXSSFSheet) sheet).trackColumnForAutoSizing(COLUMN_START_INDEX + i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void renderDataLines(ExcelSheetData data) {
|
|
|
|
|
protected void renderDataLines(ExcelSheetData data, ExcelMetadata metadata) {
|
|
|
|
|
CellStyle style = createCellStyle(workbook, false);
|
|
|
|
|
int rowIndex = ROW_START_INDEX + 1;
|
|
|
|
|
// 데이터 시작 행 (제목 행이 있으면 row 2, 없으면 row 1)
|
|
|
|
|
int rowIndex = ROW_START_INDEX + titleRowOffset + 1;
|
|
|
|
|
List<Field> fields = getAllFieldsWithExcelColumn(data.getType());
|
|
|
|
|
|
|
|
|
|
// 데이터 행 생성
|
|
|
|
|
for (Object record : data.getDataList()) {
|
|
|
|
|
Row row = sheet.createRow(rowIndex++);
|
|
|
|
|
int columnIndex = COLUMN_START_INDEX;
|
|
|
|
|
@ -54,6 +119,33 @@ public abstract class BaseSxssfExcelFile implements ExcelFile {
|
|
|
|
|
throw new RuntimeException("Error accessing data field rendering data lines.", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 각 열의 너비 조정
|
|
|
|
|
// (renderHeaders에서 이미 trackColumnForAutoSizing 호출됨)
|
|
|
|
|
for (int i = 0; i < fields.size(); i++) {
|
|
|
|
|
int columnIndex = COLUMN_START_INDEX + i;
|
|
|
|
|
Field field = fields.get(i);
|
|
|
|
|
String fieldName = field.getName();
|
|
|
|
|
|
|
|
|
|
// headerWidth가 지정되어 있으면 해당 값 사용, 없으면 자동 조정
|
|
|
|
|
int headerWidth = metadata.getHeaderWidth(fieldName);
|
|
|
|
|
if (headerWidth > 0) {
|
|
|
|
|
// headerWidth가 지정된 경우 해당 값으로 설정 (POI 단위: 1/256 문자)
|
|
|
|
|
sheet.setColumnWidth(columnIndex, headerWidth * 256);
|
|
|
|
|
} else {
|
|
|
|
|
// headerWidth가 0이면 자동 조정
|
|
|
|
|
sheet.autoSizeColumn(columnIndex);
|
|
|
|
|
|
|
|
|
|
// 한글 폰트는 autoSizeColumn이 정확하지 않으므로 여유분 추가
|
|
|
|
|
// 현재 너비의 1.3배로 설정 (최소 3000, 최대 15000)
|
|
|
|
|
int currentWidth = sheet.getColumnWidth(columnIndex);
|
|
|
|
|
int newWidth = (int) (currentWidth * 1.3);
|
|
|
|
|
|
|
|
|
|
// 최소 너비 3000 (약 11.7문자), 최대 너비 15000 (약 58.6문자)
|
|
|
|
|
newWidth = Math.max(3000, Math.min(newWidth, 15000));
|
|
|
|
|
sheet.setColumnWidth(columnIndex, newWidth);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|