Developer's Development

[JAVA] 파일 미리보기 및 다운로드 본문

개발/JAVA

[JAVA] 파일 미리보기 및 다운로드

mylee99 2023. 12. 29. 13:33

javax.crypto 파일 암/복호화 포스팅에서 이어지는 이야기

2023.11.15 - [개발/JAVA] - [JAVA] javax.crypto 파일 암/복호화

 

[JAVA] javax.crypto 파일 암/복호화

Case) 파일 업로드 시, 암호화(내용 암호화/파일명 난수 생성, 확장자 enc로 변경)해서 서버 업로드 파일 다운로드 시, 서버에 업로드된 파일 경로와 원래 파일명(확장자 포함)을 전달받아 복호화(

developer-mylee.tistory.com

 

Case)
서버에 업로드된 파일 경로와 원래 파일명(확장자 포함)을 전달받아 복호화(내용 복호화/원래 파일명, 원래 확장자로 변경)해서 미리보기 및 다운로드
이번 포스팅은 backend와 frontend 사이 파일 전송만을 목적으로 하기에, 암/복호화에 관련한 내용은 생략

 

 

1. 버튼 클릭을 통한 파일 미리보기(다운로드)

Ajax 통신을 통해 back단에서 복호화를 진행하며, 파일 객체를 전달받아 미리보기 및 다운로드 이벤트를 수행한다.

HTML
<td>
    <button th:onclick="goDownload([[${row.filePath}]], [[${row.orgFileNm}]], 'Y')" type="button">미리보기</button>
</td>
<td>
    <button th:onclick="goDownload([[${row.filePath}]], [[${row.orgFileNm}]], 'N')" type="button">다운로드</button>
</td>

<form id="fileDecryptForm">
	<input type="hidden" name="encryptedFilePath"/>
	<input type="hidden" name="decryptedFile"/>
	<input type="hidden" name="previewYn"/>
</form>
jQuery
//기존 코드에서 변경된 사항은 previewYn을 추가해 미리보기 여부를 관리하였으며, target _blank를 사용해 새창으로 띄우게 함.
var $fileDecryptForm = $("#fileDecryptForm");

goDownload = function ( filePath, orgFileNm, previewYn ) {
    $fileDecryptForm.find("[name=encryptedFilePath]").val(filePath);
    $fileDecryptForm.find("[name=decryptedFile]").val(orgFileNm);
    $fileDecryptForm.find("[name=previewYn]").val(previewYn);
    $fileDecryptForm.attr({"action":"/FileDownload","target":"_blank","method":"post"}).submit();
}
FileUtil

MIME은 "Multipurpose Internet Mail Extensions"의 약어로,
원래는 전자우편에서 사용되는 데이터 형식을 나타내기 위해 만들어졌으나 현재는 웹에서는 다양한 파일 형식을 식별하는 데 널리 사용됨.

MimeType은 파일의 종류 또는 형식을 식별하는 데 사용되는 문자열임.
정확하게 가져오는 방식은 파일의 절대 경로를 기반으로 Servlet 컨텍스트에서 MIME 타입을 얻어오는 방식이 신뢰성이 있으나,
파일 복호화 작업에 맞춰 파일명에서 MIME 타입을 추정하는 URLConnection 사용 방식으로 변경함.


- response.setContentType()
// 파일유형을 설정해 줌. MIME타입을 변경 또는 인코딩셋을 변경하고자 할 때, 해당 메소드를 사용함.
- response.setHeader()
// Content-Disposition : Http Response Body에 오는 컨텐츠의 기질/성향을 알려주는 속성임.
// attachment : 첨부파일을 의미함. fileName은 다운로드할 때의 파일이름을 의미함.

//...파일복호화 과정(Cipher) 및 Stream 관련 내용 생략
if(StringUtil.isNotEmpty( fileVo.getPreviewYn() ) && fileVo.getPreviewYn().equals( "Y" )) {
    //미리보기
    //퍼알 확장자에 따라 Content-Type 동작 설정
    String mimeType = URLConnection.guessContentTypeFromName( fileName );
    response.setContentType( mimeType );
    response.setHeader("Content-Disposition","inline; filename="+fileName);
} else {
    //다운로드
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition","attachment; filename="+fileName);
}

 

이 코드를 실행해 본 결과 pdf, jpg, png 등의 파일은 미리보기가 가능하지만, 오피스 문서 파일(ppt, xlsx 등)에 대해선 미리보기를 제공하지 않음.
미리보기를 구현하려면 브라우저가 해당 파일 형식을 렌더링할 수 있는 방식으로 파일을 전송해야 함.
파워포인트나 엑셀과 같은 파일 형식에 대해서는 미리보기를 위해 API나 JS를 사용해야 함.
숙제가 또 하나 늘었다.....

 

 

2. 버튼 클릭 없이 페이지 진입 시 이미지 파일 미리보기 형식으로 노출

이미지 암호화 및 등록 완료 후, 상세 페이지 진입 시 HTML에 각 이미지에 대한 태그를 생성한다.

HTML

Base64는 이진 데이터를 ASCII 문자로 인코딩하는 방법 중 하나로, 이를 텍스트로 변환하여 텍스트 기반의 프로토콜에서 이진 데이터를 전송하는 데 사용됨.
주로 이미지, 음성, 파일 등의 이진 데이터를 문자열로 변환할 때 활용됨.

특징
1. 문자 집합 : Base64는 64개의 문자로 이루어져 있음. 이는 대문자 26개, 소문자 26개, 숫자 10개, 그리고 '+'와 '/' 두 개의 특수문자로 이루어져 있음.
2. 패딩 : Base64는 길이가 4배의 배수가 아닌 경우 패딩을 사용하여 채움. 패딩은 '='문자로 이루어져 있음.
3. 이진 데이터 변환 : 이진 데이터를 일련의 ASCII 문자열로 변환하여 나타냄. 이를 통해 이진 데이터를 텍스트 기반의 시스템에서도 전송이 가능해짐.

<!-- 기존에 암복호화 로직 없이 업로드되어 있는 파일과의 구분을 위해 확장자 확인 th:if 조건 추가 -->
<!-- th:src 속성에 데이터 URI 스키마를 사용하여 Base64로 인코딩된 이미지 데이터를 직접 삽입, 웹 페이지에서 직접 이미지 로드 가능 -->
<th:block th:each="row : ${imgList}">
    <div>
        <img th:if="${row.fileExtn eq 'enc'}" th:src="'data:image;base64,'+${row.encodedFile}" style="max-width:150px;"/>
        <img th:unless="${row.fileExtn eq 'enc'}" th:src="|/upload/${row.filePath}|" style="max-width:150px;"/>
    </div>
</th:block>
Service (or Controller)
//파일 객체를 Base64로 인코딩하여 전달
Map<String, Object> result = new HashMap<>();

List<TestVo> imgList = testMapper.selectCmpImgList( testVo );
for( TestVo row : imgList ) {
    if( row.getFileExtn().equals( Const.ENCRYPT_EXT ) ) {
        FileVo fileVo = new FileVo();
        fileVo.setEncryptKey( secretKey );
        fileVo.setFilePath( fileUploadPath + row.getFilePath() );
        row.setEncodedFile( Base64.getEncoder().encodeToString( FileUtil.decryptFile( fileVo ) ) );
    }
}
result.put("imgList", imgList);

return result;
FileUtil
//파일 복호화 신규 메서드(byte 리턴) 추가
public static byte[] decryptFile( FileVo fileVo ) throws Exception {
    // 복호화 환경 세팅
    Key secretKey = new SecretKeySpec( fileVo.getEncryptKey().getBytes(), "AES" );
    Cipher cipher = Cipher.getInstance( "AES/ECB/PKCS5Padding" );
    cipher.init( Cipher.DECRYPT_MODE, secretKey );

    byte[] encryptedBytes = Files.readAllBytes( Paths.get( fileVo.getFilePath() ) );
    return cipher.doFinal( encryptedBytes );
}
Comments