여기 이슈는 Browser에 따라 다른 파일 다운로드 & 저장 메커니즘을 웹 서버에서 어떻게 조절하느냐를 얘기한다.
여기 언급하는 이슈 외에도 다른 여러가지 이슈가 있기 때문에
(이를테면 Jeus가 WAS일 경우 Jeus가 response header의 content disposition 값에 직접 관여하므로 이를 고려해야 하며, 이외에도 잘 모른 채 끼어들어가는 솔루션이나 개발자의 실수 등 여러 이슈가 가능)
여기 나온 해법이 전부가 아님을 일단 일러둔다.
... 코드는 다음과 같은 방식으로 구현되어야 한다.
// 빈 간, 특수문자, 한글에 대한 테스트
String filename = "바+보 는=바보.-똥개.txt";
String disposition = getDisposition(filename, "UTF-8", getBrowser(request););
char[] content = 적당히......;
response.addHeader("Content-disposition", disposition);
// 2009. 2. 19 추가
if ("Opera".equals(getBrowser(request))){
response.setContentType("application/octet-stream;charset=UTF-8");
}
Writer out = response.getWriter();
out.write(content); // <-- 자기 방식으로 넣으세요.. 대충 쓴 거니..
out.flush();
... 위의 코드에서 호출하는 메소드들이다.
private String getBrowser(HttpServletRequest request) {
String header = request.getHeader("User-Agent");
if (header.indexOf("MSIE") > -1) {
return "MSIE";
} else if (header.indexOf("Chrome") > -1) {
return "Chrome";
} else if (header.indexOf("Opera") > -1) {
return "Opera";
}
return "Firefox";
}private String getDisposition(String filename, String browser)
throws Exception {
String dispositionPrefix = "attachment;filename=";
String encodedFilename = null;
if (browser.equals("MSIE")) {
encodedFilename = URLEncoder.encode(filename, "UTF-8")
.replaceAll("\\+", "%20");
} else if (browser.equals("Firefox")) {
encodedFilename ="\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
} else if (browser.equals("Opera")) {
encodedFilename ="\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
} else if (browser.equals("Chrome")) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < filename.length(); i++) {
char c = filename.charAt(i);
if (c > '~') {
sb.append(URLEncoder.encode("" + c, "UTF-8"));
} else {
sb.append(c);
}
}
encodedFilename = sb.toString();
} else {
throw new RuntimeException("Not supported browser");
}return dispositionPrefix + encodedFilename;
}
간단히 설명하자면,
1. MSIE는 URLEncoder.encode 를 이용해 UTF-8로 인코드해 주기만 하면 된다.
(아, 공백 문제는 해결해야 함)
보통 new String(filename.getBytes("MS949"), "8859_1") 를 이용하는데,
이건 철저하게 국내용이다. 한글 Windows에서 실행한 IE/FF 라면 보통 이게 먹히겠지만
외국 로케일을 적용한 경우에는 여지없이 파일명이 깨지므로
가능하다면 어떤 경우에든 먹히는 해법을 시도하는 것이 좋다.
이게 의심된다면 자신이 만든 어플리케이션을 유닉스에서 LANG 환경변수를 바꿔가면서
시도해 보자.
(혹은 Windows라면 언어 설정을 일본어나 다른 언어로 바꾸고 시도해 보자 - 09.01.13 추가)
(이에 대한 부가설명이 http://blog.naver.com/anabaral/130042610256 에 있다 - 09.02.15 추가)
2. Firefox는 URLEncoder로 인코드한 문자열을 받아들이지 못한다.
대신 UTF-8 혹은 브라우저가 실행되는 인코딩으로 인코드된 바이너리는 받아들이므로
일반적으로 통용되는 방식을 사용한다.
new String(filename.getBytes("UTF-8"), "8859_1")
보통은 IE에서와 마찬가지로 위에 MS949를 쓰지만, 고맙게도(?) Firefox는
위의 인코딩을 UTF-8로만 설정해도 알아서 디코드 해 준다.
아마도 '브라우저 인코딩' 혹은 '기본 인코딩'으로 시도하고 인식이 안되면 UTF-8을 시도하는 듯.
다만 공백이 있으면 공백 뒤가 무시되어 잘리므로 앞뒤로 따옴표 " " 를 적용해야 한다.
3. Opera는 상당히 독특한데, Opera는 브라우저 인코딩에 맞추어 파일명을 디코드해 저장한다.
게다가 URLEncoder 로 인코드한 내용도 안 먹힌다.
그래서 이 경우는 정답이 없다.
다만 사용자가 별다른 설정을 하지 않는다면 브라우저 인코딩은 그 전까지 접했던 페이지의
인코딩을 따라가므로 우리가 일관된 인코딩 정책을 가져가고 그에 맞춰 세팅해 주면 된다.
위의 예제에서는 그 표준이 UTF-8 이었다.
최근에(2009.2.19)에 테스트한 바로는 Opera는 다운로드 당시의 ContentType 헤더에 정해준
charset에 민감하다. 그래서 Content-Type 의 charset과 문자열 인코딩의 charset을 일치
시켜주면 다운로드에 문제가 없다.
(그래서 다른 브라우저에서의 UTF-8과는 의미가 다르다)
4. 구글 Chrome도 써보았는데, 이건 URLEncoder 인코드 문자열을 받아들이지만 뭔가 부족하다.
몇몇 글자들(+, = 등)이 깨지기 때문이다.
그래서 ASCII 문자(0x00 ~ 0x7e)로 간주되는 부분들은 encode하지 않는 방향으로 진행한다.
5. Safari는 지금까지는 답이 없다..
무조건 latin1(8859_1)으로 인식하니..
이 예제의 테스트는
Internet Explorer 6,
Internet Explorer 7,
Firefox 2.0,
Firefox 3.0,
Opera 9.62,
Chrome 1.0
( 실패했지만 Safari 3.2.1)
에서 진행했다.
위의 테스트로 빈 칸, 특수문자, 한글에 대한 테스트가 가능했다.
테스트에 협조해 주신 liquidbird 님께 심심한 감사를 표한다.
[출처] 자바 웹 어플리케이션에서 파일 다운로드 시 한글명 깨짐 이슈 해결법|작성자 우가가