file.encoding 이 US-ASCII 에서 UTF-8 파일 이름 처리방법
1. 개요
file.encoding 이 US-ASCII 인데, (한글과 같이) US-ASCII 을 벗어나는 문자열을 처리한다면, 파라미터 뿐만 아니라 파일이름도 처리해야 한다.
System.getProperty("file.encoding")
을 출력하면,ANSI_X3.4-1968
와 같은 것일 수도 있지만,java.nio.charset.Charset.forName("US-ASCII").aliases()
를 출력해 보면,ANSI_X3.4-1968
와 US-ASCII 는 같은 것이다.
파라미터는 request.getParameter 와 인코딩(Graha 라이브러리의 처리방법을 중심으로) 을 참조하면 되고, 이 글에서는 파일이름 처리에 대해서만 다루기로 한다.
file.encoding 이 MS-949 이거나 혹은 다른 것이라도 처리방법은 같다.
2. 파일목록을 가져오는 경우
java.io.File.listFiles() 을 이용해서 파일이름을 가져오면 한글은 전부 다 깨진다.
file.encoding 을 변경할 수 없다면, 다음과 같이 DirectoryStream 을 이용해서 한다.
String filePath = "/"; DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(filePath)); for(Path path : stream) { String fileName = java.net.URLDecoder.decode(path.toUri().toString().substring(path.toUri().toString().lastIndexOf("/") + 1).replace("+", "%2B"), "UTF-8"); } stream.close();
DirectoryStream 은 명시적으로 close 해야 하고, 만일 이를 누락한다면, 언젠가는 too many open files 에러가 발생하게 된다.
java.io.File 을 대신해서 Files 를 사용해야 하는데, 많이 사용되는 method 는 다음과 같다.
- Files.exists(path) : 파일이 존재하는지 여부
- Files.isDirectory(path) : 디렉토리인지 여부
- Files.isRegularFile(path) : 일반 파일인지 여부
- Files.size(path) : 파일 크기
- Files.getLastModifiedTime(path).toMillis() : 최종 수정일시
3. 파일을 가져오는 경우
new java.io.File("한글 이름 파일.pdf")
과 같이 파일을 가져오려고 시도하면,
FileNotFoundException 이 발생한다.
유효한 Path 를 얻은 경우에도, new FileInputStream(Path.toFile()) 혹은 Path.toUri().toURL().openStream() 과 같은 방식으로 InputStream 을 얻을수는 없다.
이를 해결하기 위해서는 다음과 같이 처리해야 한다.
Path path = Paths.get(new URI("file://" + basePath + java.io.File.separator + java.net.URLEncoder.encode("한글 이름 파일.pdf", "UTF-8").replaceAll("\\+", "%20"))); if(Files.exists(path)) { InputStream fis = Files.newInputStream(path); fis.close(); }
파일이 존재하는지 검사하는 것도 java.io.File.exists() 는 안되고, Files.exists() 를 사용해야 한다.
4. 파일을 저장하는 경우
new java.io.File("한글 이름 파일.pdf")
과 같이 파일을 저장한다면,
(Exception 이 발생하지는 않지만)
한글은 모두 깨져서 물음표(?)로 저장된다.
이를 해결하기 위해서는 다음과 같이 처리해야 한다.
URI uri = new URI("file://" + basePath + java.io.File.separator + java.net.URLEncoder.encode("한글 이름 파일.pdf", "UTF-8").replaceAll("\\+", "%20")); if(fileItem.isInMemory()) { Files.write(Paths.get(uri), fileItem.get()); } else { Files.move(((DiskFileItem)fileItem).getStoreLocation().toPath(), Paths.get(uri)); }
fileItem 변수는 Apache Commons FileUpload 의 FileItem 이다.
상황에 따라 Files.newOutputStream(Paths.get(uri))
혹은 Files.copy() 메소드를 사용해야 한다.
OutputStream 이 필요하다면, 다음과 같이 해야 한다.
Files.newOutputStream(Paths.get(uri));
5. JavaMail API 에서
이메일을 보낼때, 파일이름에 한글이 포함된 첨부파일을 붙여야 한다면, Path 를 파라미터로 받는 생성자를 가진 javax.activation.DataSource 의 구현체가 있어야 한다.
6. "+" 에 대해서
java.net.URLEncoder.encode 는 공백(" ") 을 "+" 로 변환하고, java.net.URLDecoder.decode 는 (반대로) "+" 를 공백(" ") 으로 변환한다.
따라서 java.net.URLEncoder.encode 를 호출한 이후에 "+" 를 "%20" 으로 변경해야 하고, (반대로) java.net.URLDecoder.decode 전에 "+" 를 "%2B" 로 변경해야 한다.