You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
7.7 KiB
Java
189 lines
7.7 KiB
Java
package egovframework.util.excel;
|
|
|
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.URLEncoder;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
/**
|
|
* 단일 시트 엑셀 파일 생성 클래스
|
|
*
|
|
* <p>이 클래스는 {@link BaseSxssfExcelFile}을 상속하여 단일 시트 엑셀 파일을 생성합니다.
|
|
* HTTP 응답으로 엑셀 파일을 다운로드하거나, OutputStream으로 파일을 출력할 수 있습니다.</p>
|
|
*
|
|
* <p><b>사용 예제 1: HTTP 응답으로 엑셀 다운로드</b></p>
|
|
* <pre>{@code
|
|
* @GetMapping("/download.xlsx")
|
|
* public void downloadExcel(HttpServletRequest request, HttpServletResponse response) {
|
|
* List<UserVO> dataList = userService.selectUserList();
|
|
* ExcelSheetData sheetData = ExcelSheetData.of(dataList, UserVO.class, "사용자 목록");
|
|
* new SxssfExcelFile(sheetData, request, response, "사용자목록.xlsx");
|
|
* }
|
|
* }</pre>
|
|
*
|
|
* <p><b>사용 예제 2: 암호화된 엑셀 파일 다운로드</b></p>
|
|
* <pre>{@code
|
|
* @GetMapping("/download-encrypted.xlsx")
|
|
* public void downloadEncryptedExcel(HttpServletRequest request, HttpServletResponse response) {
|
|
* List<UserVO> dataList = userService.selectUserList();
|
|
* ExcelSheetData sheetData = ExcelSheetData.of(dataList, UserVO.class, "사용자 목록");
|
|
* new SxssfExcelFile(sheetData, request, response, "사용자목록.xlsx", "password123");
|
|
* }
|
|
* }</pre>
|
|
*
|
|
* <p><b>사용 예제 3: OutputStream으로 출력</b></p>
|
|
* <pre>{@code
|
|
* try (FileOutputStream fos = new FileOutputStream("output.xlsx")) {
|
|
* List<UserVO> dataList = userService.selectUserList();
|
|
* ExcelSheetData sheetData = ExcelSheetData.of(dataList, UserVO.class);
|
|
* new SxssfExcelFile(sheetData, fos, null);
|
|
* }
|
|
* }</pre>
|
|
*
|
|
* <p><b>VO 클래스 예제:</b></p>
|
|
* <pre>{@code
|
|
* @ExcelSheet(name = "사용자")
|
|
* public class UserVO {
|
|
* @ExcelColumn(headerName = "이름", headerWidth = 20)
|
|
* private String userName;
|
|
*
|
|
* @ExcelColumn(headerName = "이메일", headerWidth = 30)
|
|
* private String email;
|
|
*
|
|
* @ExcelColumn(headerName = "전화번호")
|
|
* private String phone;
|
|
* }
|
|
* }</pre>
|
|
*
|
|
* @see BaseSxssfExcelFile
|
|
* @see ExcelSheetData
|
|
* @see ExcelColumn
|
|
* @author eGovFrame
|
|
*/
|
|
public class SxssfExcelFile extends BaseSxssfExcelFile {
|
|
|
|
// ==================== 생성자 (HTTP 응답 다운로드) ====================
|
|
|
|
/**
|
|
* HTTP 응답으로 엑셀 파일을 다운로드합니다 (암호화 없음).
|
|
*
|
|
* <p>이 생성자는 암호화 없이 엑셀 파일을 HTTP 응답으로 다운로드합니다.
|
|
* 파일명은 브라우저 종류에 따라 적절하게 인코딩됩니다.</p>
|
|
*
|
|
* @param data 엑셀 시트 데이터 (데이터 리스트, 타입, 제목)
|
|
* @param request HTTP 요청 객체 (브라우저 정보 확인용)
|
|
* @param response HTTP 응답 객체
|
|
* @param fileName 다운로드될 파일명 (확장자 포함, 예: "사용자목록.xlsx")
|
|
* @throws RuntimeException 파일 생성 또는 출력 중 오류 발생 시
|
|
*/
|
|
public SxssfExcelFile(ExcelSheetData data, HttpServletRequest request, HttpServletResponse response,
|
|
String fileName) {
|
|
this(data, request, response, fileName, null);
|
|
}
|
|
|
|
/**
|
|
* HTTP 응답으로 엑셀 파일을 다운로드합니다 (암호화 지원).
|
|
*
|
|
* <p>이 생성자는 선택적으로 암호화하여 엑셀 파일을 HTTP 응답으로 다운로드합니다.
|
|
* 파일명은 브라우저 종류에 따라 적절하게 인코딩됩니다.</p>
|
|
*
|
|
* @param data 엑셀 시트 데이터 (데이터 리스트, 타입, 제목)
|
|
* @param request HTTP 요청 객체 (브라우저 정보 확인용)
|
|
* @param response HTTP 응답 객체
|
|
* @param fileName 다운로드될 파일명 (확장자 포함, 예: "사용자목록.xlsx")
|
|
* @param password 암호화 비밀번호 (null이면 암호화하지 않음)
|
|
* @throws RuntimeException 파일 생성 또는 출력 중 오류 발생 시
|
|
*/
|
|
public SxssfExcelFile(ExcelSheetData data, HttpServletRequest request, HttpServletResponse response,
|
|
String fileName, @Nullable String password) {
|
|
try {
|
|
setDownloadHeaders(request, response, fileName);
|
|
ExcelMetadata metadata = ExcelMetadataFactory.getInstance().createMetadata(data.getType());
|
|
renderSheetContent(data, metadata);
|
|
writeWithEncryption(response.getOutputStream(), password);
|
|
} catch (IOException e) {
|
|
throw new RuntimeException("HTTP 응답으로 엑셀 파일 출력 중 오류가 발생했습니다.", e);
|
|
}
|
|
}
|
|
|
|
// ==================== 생성자 (OutputStream 출력) ====================
|
|
|
|
/**
|
|
* OutputStream으로 엑셀 파일을 출력합니다.
|
|
*
|
|
* <p>이 생성자는 선택적으로 암호화하여 엑셀 파일을 OutputStream으로 출력합니다.
|
|
* 파일 시스템에 직접 저장하거나 다른 스트림 처리 용도로 사용할 수 있습니다.</p>
|
|
*
|
|
* @param data 엑셀 시트 데이터 (데이터 리스트, 타입, 제목)
|
|
* @param outputStream 출력 스트림
|
|
* @param password 암호화 비밀번호 (null이면 암호화하지 않음)
|
|
* @throws RuntimeException 파일 생성 또는 출력 중 오류 발생 시
|
|
*/
|
|
public SxssfExcelFile(ExcelSheetData data, OutputStream outputStream, @Nullable String password) {
|
|
ExcelMetadata metadata = ExcelMetadataFactory.getInstance().createMetadata(data.getType());
|
|
renderSheetContent(data, metadata);
|
|
writeWithEncryption(outputStream, password);
|
|
}
|
|
|
|
// ==================== private 헬퍼 메서드 ====================
|
|
|
|
/**
|
|
* HTTP 응답 헤더를 설정하여 엑셀 파일 다운로드를 준비합니다.
|
|
*
|
|
* <p>브라우저 종류에 따라 파일명을 적절하게 인코딩하여 Content-Disposition 헤더를 설정합니다:
|
|
* <ul>
|
|
* <li>IE (MSIE/Trident): UTF-8 URL 인코딩 (+ 공백 처리)</li>
|
|
* <li>기타 브라우저: UTF-8 → ISO-8859-1 변환</li>
|
|
* </ul>
|
|
* </p>
|
|
*
|
|
* @param request HTTP 요청 객체 (User-Agent 헤더 확인용)
|
|
* @param response HTTP 응답 객체
|
|
* @param fileName 다운로드될 파일명
|
|
* @throws RuntimeException 인코딩 오류 발생 시
|
|
*/
|
|
private void setDownloadHeaders(HttpServletRequest request, HttpServletResponse response, String fileName) {
|
|
try {
|
|
String browser = request.getHeader("User-Agent");
|
|
String encodedFileName = encodeFileName(fileName, browser);
|
|
|
|
response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
|
|
} catch (UnsupportedEncodingException e) {
|
|
throw new RuntimeException("파일명 인코딩 중 오류가 발생했습니다.", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 브라우저에 맞게 파일명을 인코딩합니다.
|
|
*
|
|
* @param fileName 원본 파일명
|
|
* @param userAgent User-Agent 헤더 값
|
|
* @return 인코딩된 파일명
|
|
* @throws UnsupportedEncodingException 인코딩 실패 시
|
|
*/
|
|
private String encodeFileName(String fileName, String userAgent) throws UnsupportedEncodingException {
|
|
if (isInternetExplorer(userAgent)) {
|
|
// IE: UTF-8 URL 인코딩 (+ -> 공백 처리)
|
|
return URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
|
|
} else {
|
|
// Chrome, Firefox 등: UTF-8 바이트를 ISO-8859-1로 변환
|
|
return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* User-Agent에서 Internet Explorer 브라우저인지 확인합니다.
|
|
*
|
|
* @param userAgent User-Agent 헤더 값
|
|
* @return IE인 경우 true, 아니면 false
|
|
*/
|
|
private boolean isInternetExplorer(String userAgent) {
|
|
return userAgent != null && (userAgent.contains("MSIE") || userAgent.contains("Trident"));
|
|
}
|
|
}
|