Boa tarde Srs,
Estou com um problema de encoding que está tirando minhas noites de sono… já pesquisei pra caramba, mas ainda não consegui achar onde estou pecando.
O problema é o seguinte, tenho uma rotina que exporta alguns dados do banco de dados (DB2, UTF-8 ) e gera um arquivo CSV, só que este arquivo chega pro usuário baixar como UTF-8 e o Excel não o interpreta corretamente.
Como se dá a exportação:
Basicamente, é criado um StringBuilder com construtor sem parâmetros, depois são concatenado os títulos das colunas com tokens de intercionalização para pt_BR (verifiquei o arquivo de tokens e segundo o Notepad++ o encoding dele é ANSI).
Depois adicionando registro por registro do banco à este StringBuilder e retornado um toString deste StringBuilder.
Aí começa o vilão da história (ou pelo menos quem eu acho que é o vilão).
Essa String passa por um tratamento (um tanto quanto de choque hehehe).
Segue o código deste tratamento… :
A chamada para o método de tratamento:
String csv2 = AUtil.autoEncode(csv.getBytes());
public static String autoEncode(byte[] b) {
String encodeString = "";
if (b == null) {
return encodeString;
}
try {
CharsetToolkit toolkit = new CharsetToolkit(b);
toolkit.setEnforce8Bit(true);
if ("UTF-8".equals(toolkit.guessEncoding().name())) {
encodeString = new String(b, "UTF-8");
} else if ("ISO-8859-1".equals(toolkit.guessEncoding().name())) {
encodeString = new String(b, "ISO-8859-1");
} else {
encodeString = new String(b);
}
} catch (Exception e) {
Logger.error("[autoEncode] " + e.getMessage());
}
return encodeString;
}
O construtor da CharsetToolkit não faz nada, só atribui b a uma variável cde instância da classe, e o método guessEncoding tenta adivinhar qual o encoding está sendo usado (avaliando os 5 primeiros bytes), no meu ambiente Windows(pt_BR) ele retorna windows-1252 e no meu ambiente Linux(en_US) ele retorna ISO-8859-1.
O System.getProperty(“file.encoding”) no Windows retorna CP1252 e no Linux retorna ISO-8859-1.
Bom, continuando o processo, depois do tratamento nesta string, temos o código responsável por enviar a mesma para o usuário:
response.addHeader("Cache-Control", "no-cache");
response.addHeader("Pragma", "no-cache");
response.addIntHeader("Expires", 0);
response.setContentType("application/csv");
response.setHeader("Content-Disposition", "filename=arquivo.csv;");
response.setContentLength(csv2.length());
PrintWriter writer = response.getWriter();
writer.print(csv2);
writer.flush();
writer.close();
Fiz alguns testes:
- Removi a chamada do método, deixando apenas
String csv2 = csv;
- Coloquei mensagens de debug para saber o estado do writer, que retornaram o seguinte resultado no ambiente Windows:
- response.getLocale() retornou: “pt_BR”;
- response.getCharacterEncoding() retornou: “ISO-8859-1”.
Isto tudo roda debaixo do tomcat6.0.14.
A versão 1 deste sistema roda um código semelhante a esse, considerando que a String não passa pelo tratamento do método autoEncode e exporta um CSV com encoding ANSI segundo o Notepad++ e é exibido perfeitamento pelo Excel. Acho que a diferença gritante entre as duas, é que não é o tomcat quem responde diretamente as requisições, e sim um Apache.
Preciso exportar de forma que o Excel reconheça, mas não posso forçar nada porque esse sistema roda em diversas plataformas e atende clientes de diversas localidades (até ásia).
Alguma sugestão?