Problema de encoding criando arquivo CSV  XML
Índice dos Fóruns » Desenvolvimento Web
Autor Mensagem
leandrodelsole
What is classpath?

Membro desde: 20/08/2009 16:49:37
Mensagens: 7
Offline

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:





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:



Fiz alguns testes:

- Removi a chamada do método, deixando apenas


- 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?

This message was edited 1 time. Last update was at 20/08/2009 17:33:50

renatosilva
GUJ Master

Membro desde: 16/12/2004 17:09:19
Mensagens: 1787
Offline

Qual o problema com o toString() do StringBuilder que faz você querer fazer isso tudo?

This message was edited 1 time. Last update was at 21/08/2009 16:09:46

leandrodelsole
What is classpath?

Membro desde: 20/08/2009 16:49:37
Mensagens: 7
Offline

Renato,

Esse é um código que estou dando manutenção, não entendi o propósito de todo este tratamento também. A principío achei que eles tivessem convertendo, mas de acordo com os testes em ambientes onde caiu em todas possibilidades do if-elseif-else e não houve diferença no resultado retornado.

Como citei acima, eu testei eliminar a chamada ao método que faz todo este tratamento e apenas considerar a String retornada pelo métotod toString() do StringBuilder, mas o problema persistiu, o CSV continuou vindo com caracteres como "acentuação" no lugar de acentos...
renatosilva
GUJ Master

Membro desde: 16/12/2004 17:09:19
Mensagens: 1787
Offline

"acentuação" acontece quando um conjunto de bytes UTF-8 é decodificado como ISO-8859-1. Pelo o que entendi, os bytes UTF-8 do banco estão sendo decodificados erroneamente com o encoding do sistema, por alguma camada do código, talvez sua biblioteca de acesso ao banco. Use um depurador para visualizar as strings logo após serem recuperadas do banco.

Caso você não consiga de jeito nenhum trazer as strings do banco corretamente, você pode tentar isso antes de escrever o CSV:

leandrodelsole
What is classpath?

Membro desde: 20/08/2009 16:49:37
Mensagens: 7
Offline

Renato,

O problema é exatamente este (a conversão de carácteres UTF-8 para ISO-8859-1), já tinha constatado isso mas obrigado pela ajuda.

Sobre sua sugestão do new String, vale lembrar que aquele método autoEncode faz exatamente isso, já testei fazer isso de 3 formas (UTF-8, ISO-8859-1 e windows-1252). Mas não funcionou.

O response.getWriter() retorna por default um Writer em ISO-8859-1... coloquei uma mensagem de debug e olha lá:


Produz saída "ISO-8859-1".

Então... alterei com qual encoding o Writer deveria vir, usando este método:
http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/ServletResponse.html#setContentType%28java.lang.String%29

Beleza, veio em UTF-8 o Writer... só que o parse continua a dar problema na hora que abre o arquivo pelo Excel, ele deve ser burro e tenta transformar UTF-8 em ANSI ou algo que o valha, ao invés de lê-lo como UTF-8 mesmo.

Aí lembrei que eu já tinha testado isso, mesmo o Writer retornando ISO-8859-1, o arquivo chegava como UTF-8 segundo meu Notepad++.

Tentei utilizar o método encode/decode do Charset, mas sem muito sucesso...


Sempre chega como UTF-8 aqui...

A solução mesmo acho que está em como converter carácteres UTF-8 para ISO-8859-1 de forma válida (que todos carácteres fiquem consistente), o problema é: como?

Ou então, será que tem como dizer pro Excel que ele tem que abrir aquele arquivo como UTF-8 direito?

Sugestões?

leandrodelsole
What is classpath?

Membro desde: 20/08/2009 16:49:37
Mensagens: 7
Offline

Ah claro, só uma observação.

O java sempre trata o objeto String como unicode


Acho que isso diz muito do porque sempre chega como UTF-8. Na verdade então o que eu preciso é uma forma de mandar para download os bytes, e não um toString dos bytes...
leandrodelsole
What is classpath?

Membro desde: 20/08/2009 16:49:37
Mensagens: 7
Offline

Renato,

Problema resolvido!

Ao invés de usar


que imprime apenas a String, portanto, sempre imprimirá como Unicode (segundo meu Notepad++, UTF-8 (sem bom)), passei a usar o


que imprime os bytes, ou seja, imprime no encoding especificado.

Vale lembrar que o Charset dele será definido ou pelo response.setContentType ou será ISO-8859-1 (o default), como o meu caso é um arquivo para download, o Charset dele não influencia (na verdade, pelo que entendo, especifícar o Charset é para avisar ao browser o que está sendo enviado, para ele poder renderizar de maneira correta, ou seja, no caso de arquivo para download o browser não mete a mão).

Eu converti os bytes usando o seguinte:


Obs1.: encode é uma String que vem de um arquivo de propriedades.
Obs2.: ByteBuffer tem um método array(), que retorna um byte [] (para imprimir no out).

Usei este tópico como referência, ajudou bastante:
http://www.guj.com.br/posts/list/12456.java

Valeu Renato pela boa vontade e as boas dicas

Leandro Del Sole

This message was edited 1 time. Last update was at 24/08/2009 15:08:52

Marlon Polo
Smalltalk

Membro desde: 08/07/2008 07:22:41
Mensagens: 1
Offline

Olá a todos,

Estou tendo um problema que não envolve o encoding mas sim o tamanho do arquivo csv que
o usuário irá fazer o download.

Fiz um pouco diferente do leandrodelsole, tal que pego os caracteres de um textFieldArea para salvar.
Usei como base esse post também: http://www.guj.com.br/posts/list/75335.java

O código é o sequinte:

o log do tomcat infoma o seguinte erro, por exemplo, quando tento salvar um arquivo com 2897 linhas e 1727419 caractes

02/05/2010 15:42:57 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet Faces Servlet threw exception
java.lang.IllegalStateException: Cannot forward after response has been committed
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:312)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
at com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:40
at com.sun.faces.application.ViewHandlerImpl.executePageToBuildView(ViewHandlerImpl.java:439)
at com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:114)
at com.sun.rave.web.ui.appbase.faces.ViewHandlerImpl.renderView(ViewHandlerImpl.java:320)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:106)
at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:144)
at com.sun.faces.extensions.avatar.lifecycle.PartialTraversalLifecycle.render(PartialTraversalLifecycle.java:106)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.sun.webui.jsf.util.UploadFilter.doFilter(UploadFilter.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:12
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
at java.lang.Thread.run(Thread.java:619)


Aguardo
Obrigado

This message was edited 2 times. Last update was at 02/05/2010 15:40:19

leandrodelsole
What is classpath?

Membro desde: 20/08/2009 16:49:37
Mensagens: 7
Offline

Bom dia Marlon,

Primeiramente, por ser um erro diferente, acho que você deveria abrir um novo tópico.

Bom, o exceção me pareceu bastente descritiva.
Joguei-a no google e veio este tópico do guj: http://www.guj.com.br/posts/list/29284.java

atente-se ao trecho
Há uma suspeita que seja problema com o forward() do servlet ou flush() do response


dê uma olhada se esse seu .flush() não é o problema hehehe
 
Índice dos Fóruns » Desenvolvimento Web
Ir para:   
Powered by JForum 2.1.8 © JForum Team