JSP para servir um zip corrompe o arquivo
Pergunta
Estou tendo um problema ao tentar servir um arquivo zip em um JSP.
O arquivo zip é sempre corrupto após ele tem download acabado. Eu tentei alguns métodos diferentes para leitura e escrita, e nenhum deles parece fazer o truque.
Eu acho que ele provavelmente está adicionando em caracteres ASCII em algum lugar como o arquivo será aberto e exibirá todos os nomes de arquivos, mas não posso extrair os arquivos.
Aqui está o meu código mais recente:
<%@ page import= "java.io.*" %>
<%
BufferedReader bufferedReader = null;
String zipLocation = "C:\\zipfile.zip";
try
{
bufferedReader = new BufferedReader(new FileReader(zipLocation));
response.setContentType("application/zip");
response.setHeader( "Content-Disposition", "attachment; filename=zipfile.zip" );
int anInt = 0;
while((anInt = bufferedReader.read()) != -1)
{
out.write(anInt);
}
}
catch(Exception e)
{
e.printStackTrace();
}
%>
EDIT: Mudei o código para um servlet e ainda não funcionou. Eu mudei um monte mais coisas ao redor, então aqui está a última versão do código não-trabalho:
public void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
{
try
{
String templateLocation = Config.getInstance().getString("Site.templateDirectory");
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=output.zip;");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(baos);
FileInputStream fis = new FileInputStream(templateLocation);
int len;
byte[] buf = new byte[1024];
while ((len = fis.read(buf)) > 0)
{
bos.write(buf, 0, len);
}
bos.close();
PrintWriter pr = response.getWriter();
pr.write(baos.toString());
pr.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
EDIT2:
Este é o código servlet que realmente funciona. Obrigado a todos!
public void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException
{
try
{
String templateLocation = Config.getInstance().getString("Site.templateDirectory");
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=output.zip;");
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
FileInputStream fis = new FileInputStream(templateLocation);
int len;
byte[] buf = new byte[1024];
while ((len = fis.read(buf)) > 0)
{
bos.write(buf, 0, len);
}
bos.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
Solução
Zips são arquivos binários e, portanto, não é adequado para ser transferido como dados de caracteres. Também texto fora do código pode corromper o arquivo.
Use, um servlet simples baunilha em vez do JSP.
Outras dicas
O JSP acrescenta espaços em branco para a saída. Eu sugiro que você se move a um servlet.
Como alternativa, você pode dar uma olhada em Retira espaço jsp saída, mas Eu não tenho certeza de que não iria afectar a própria saída ZIP.
O problema neste trecho exata é na linha dois - você tem uma linha em branco fora tags JSP, que é enviado para o navegador como HTML (ou seja, uma linha em branco em HTML).
Tenha muito cuidado para remover qualquer coisa que não está em <%%>, especialmente em branco (mesmo uma nova linha de terminais!), Ou seguir o conselho em outro lugar e usar um servlet:)
Seu código servlet não funciona porque você está a saída de seu fluxo para response.getWriter()
, que, como outros têm para fora pontas é para dados caracteres . Cito o Javadoc:
PrintWriter getWriter() throws IOException
Retorna um objeto PrintWriter que podem enviar mensagens de texto de caracteres para o cliente. O PrintWriter usa a codificação de caracteres retornado por getCharacterEncoding ()
Não só isso, mas você não está mesmo escrevendo a matriz de bytes para o Writer
, você está escrevendo os resultados de ByteArrayOutputStream.toString()
, que também faz uma conversão de caráter ~
Você quer usar response.getOutputStream (), algo como:
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
E, em seguida, escrever o conteúdo de bytes de seu arquivo a ele.
Os leitores são tipicamente para fluxos de caracteres se bem me lembro - que você pode tentar algo mais ao longo das linhas de um ByteArrayOutputStream? Além disso, o JSP poderia realmente estar corrompendo a saída como outros têm comentado.
bufferedReader = new BufferedReader(new FileReader(zipLocation));
Além da questão Servlet / JSP, esta linha está corrompendo seus dados com 100% de certeza. Ele vai tentar interpretar os dados binários como texto usando a codificação padrão da plataforma, o que significa que cerca de metade dos bytes no arquivo são substituídos por "caráter desconhecido".
Bytes e cordas não são a mesma coisa!
Eu também sugeriria usar um servlet simples e consideraria resposta de Robert Munteanu como certo, mas você poderia fazer isso em seu JSP também. O problema não é o espaço em branco adicionado, mas que a variável implícito "out" é uma instância de JspWriter e um escritor é para os personagens, não para bytes. Tente usar response.getOutputStream () e direito sobre ele, que deve funcionar. Você receberá uma exceção dizendo que outra pessoa já tem o OutputStream, mas você pode ignorá-lo.
Mas como eu disse, usando um servlet seria muito mais limpo.