Swing이나 AWT로 간단한 도구를 만들다 보면 "파일 선택 창 띄워서 하나 고르게 하고, 처리한 결과를 파일로 저장한 다음 열어 보여주기" 흐름이 자주 필요합니다. 인증서 파일을 파싱해서 .der로 저장하고 윈도우 기본 뷰어로 바로 띄우는 식의 유틸리티가 딱 그런 경우입니다. 여기서는 그 세 단계를 나눠서 봅니다.
파일 선택 창 띄우기
java.awt.FileDialog는 운영체제 기본 파일 열기 창을 그대로 씁니다. Swing의 JFileChooser보다 생김새가 윈도우 탐색기와 같아서 사용자가 익숙해한다는 장점이 있습니다. 필요한 import는 두 개입니다.
import java.awt.FileDialog;
import java.awt.Frame;
FileDialog는 부모 Frame이 있어야 만들 수 있어서, 화면에 띄울 창이 따로 없으면 빈 Frame을 하나 만들어 넘깁니다. 세 번째 인자 FileDialog.LOAD가 열기 모드이고, 저장 창이 필요하면 FileDialog.SAVE를 씁니다.
Frame f = new Frame();
FileDialog dial = new FileDialog(f, "Open", FileDialog.LOAD);
dial.setFile("*.*");
dial.setVisible(true);
String DirName = dial.getDirectory();
String FileName = dial.getFile();
if(m_Debug == true) System.out.println(DirName + FileName);
String strName = DirName + FileName;
// File Load
byte[] abData = FileUtil.read(strName);
setVisible(true)는 모달이라 사용자가 파일을 고르거나 취소할 때까지 다음 줄로 넘어가지 않습니다. 선택이 끝나면 getDirectory()가 폴더 경로(끝에 구분자 포함), getFile()이 파일 이름을 돌려주니 둘을 이어 붙이면 전체 경로가 됩니다.
실제로 돌려 보면 막히는 지점이 하나 있습니다. 사용자가 취소를 누르면 getFile()이 null을 돌려줍니다. 위 코드처럼 바로 이어 붙이면 경로가 "nullnull" 같은 문자열이 되고 읽기 단계에서 엉뚱한 예외가 납니다. FileName == null이면 바로 빠져나가는 검사를 꼭 넣어 주세요.
FileUtil.read()는 표준 라이브러리 메서드가 아니라 원 작성자가 만든 유틸 클래스입니다. 같은 일을 표준 API로 하려면 Java 7 이상에서 java.nio.file.Files.readAllBytes(Paths.get(strName))를 쓰면 됩니다. m_Debug도 디버그 출력을 켜고 끄려고 둔 필드라 본인 코드에 맞게 바꾸면 됩니다.
byte 배열을 파일로 저장하는 함수
경로 문자열과 byte[]를 받아 파일로 쓰는 함수입니다. 성공하면 true, 어떤 예외든 나면 false를 돌려줍니다.
public static boolean save(final String str, final byte[] by)
{
try
{
final FileOutputStream fos = new FileOutputStream(str);
fos.write(by);
fos.close();
return true;
}
catch (Exception e)
{
return false;
}
}
단순해서 쓰기 편하지만 주의할 부분이 있습니다. write() 도중 예외가 나면 close()까지 가지 못해서 파일 핸들이 열린 채로 남습니다. 윈도우에서는 이 상태로 같은 파일을 다시 쓰거나 지우려 하면 "다른 프로세스가 사용 중" 오류가 납니다. Java 7 이상이라면 try-with-resources로 바꿔 두는 게 안전합니다. 아래는 그렇게 고친 버전입니다.
public static boolean save(final String str, final byte[] by)
{
try (FileOutputStream fos = new FileOutputStream(str))
{
fos.write(by);
return true;
}
catch (Exception e)
{
return false;
}
}
예외를 삼키고 false만 돌려주는 방식은 원인 파악이 어렵다는 단점도 있습니다. 적어도 e.printStackTrace()나 로그 한 줄은 남겨 두면 나중에 경로 오타인지 권한 문제인지 바로 구분됩니다.
저장한 파일을 연결 프로그램으로 열기
마지막은 저장한 파일을 바로 실행하는 부분입니다. 원문은 서명 데이터에서 인증서를 뽑아 .der 파일로 저장하고 여는 예제였습니다.
byte[] abCert = signVerify.ParseSignedData(abPureSignData);
//File Save
UFilePlus.save("D:/test.der", abCert);
//Run
String cmd[] = {"cmd", "/c", "start", "D:/test.der"};
Runtime.getRuntime().exec(cmd);
signVerify와 UFilePlus 역시 원 작성자 프로젝트에 있던 클래스이고, UFilePlus.save는 위에서 본 save 함수와 같은 역할입니다. 핵심은 마지막 두 줄입니다. cmd /c start 파일경로를 실행하면 윈도우가 확장자에 연결된 프로그램으로 파일을 열어 줍니다. .der면 인증서 뷰어가, .txt면 메모장이 뜨는 식입니다.
이 방식은 윈도우 전용입니다. 여러 운영체제에서 돌아야 한다면 java.awt.Desktop.getDesktop().open(new File(경로))가 같은 일을 OS에 맞게 처리해 줍니다. 그리고 start 명령은 경로에 공백이 있으면 첫 번째 따옴표 인자를 창 제목으로 해석하는 특성이 있어서, 공백 있는 경로를 다룰 때는 이 점도 미리 확인해 보는 게 좋습니다.
참고: 悲像之我 - [Java] File Dialog, File Open, File Read, File Save