Criando arquivo zip a partir do fluxo e baixando -o
Pergunta
Eu tenho um datatable que eu quero convertê -lo para XML e depois zombar, usando o DotNetzip. Finalmente, o usuário pode baixá -lo via página da web ASP.NET. Meu código abaixo
dt.TableName = "Declaration";
MemoryStream stream = new MemoryStream();
dt.WriteXml(stream);
ZipFile zipFile = new ZipFile();
zipFile.AddEntry("Report.xml", "", stream);
Response.ClearContent();
Response.ClearHeaders();
Response.AppendHeader("content-disposition", "attachment; filename=Report.zip");
zipFile.Save(Response.OutputStream);
//Response.Write(zipstream);
zipFile.Dispose();
O arquivo XML no arquivo zip está vazio.
Solução
2 coisas. Primeiro, se você mantiver o design do código que você possui, precisará executar um Seek () no MemoryStream antes de escrevê -lo na entrada.
dt.TableName = "Declaration";
MemoryStream stream = new MemoryStream();
dt.WriteXml(stream);
stream.Seek(0,SeekOrigin.Begin); // <-- must do this after writing the stream!
using (ZipFile zipFile = new ZipFile())
{
zipFile.AddEntry("Report.xml", "", stream);
Response.ClearContent();
Response.ClearHeaders();
Response.AppendHeader("content-disposition", "attachment; filename=Report.zip");
zipFile.Save(Response.OutputStream);
}
Mesmo se você mantiver esse design, eu sugeriria uma cláusula de uso (), como mostrei, e como descrito em todos os Exemplos de Dotnetzip, em vez de chamar DISPOSE (). A cláusula de uso () é mais confiável diante das falhas.
Agora você pode se perguntar: por que é necessário procurar no MemoryStream antes de ligar para Addentry ()? O motivo é que addentry () foi projetado para apoiar os chamadores que passam por um fluxo onde a posição é importante. Nesse caso, o chamador precisa que os dados de entrada sejam lidos no fluxo, usando a posição atual do fluxo. Addentry () suporta isso. Portanto, defina a posição no fluxo antes de chamar addEntry ().
Mas, melhor a opção é modificar seu código para usar o Sobrecarga de addEntry () que aceita umlegate. Foi projetado especificamente para adicionar conjuntos de dados em arquivos ZIP. Seu código original grava o conjunto de dados em um fluxo de memória e, em seguida, procura no fluxo e grava o conteúdo do fluxo no zip. É mais rápido e mais fácil se você escrever os dados uma vez, que é o que o WritedElegate permite que você faça. O código se parece com o seguinte:
dt.TableName = "Declaration";
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/zip";
Response.AppendHeader("content-disposition", "attachment; filename=Report.zip");
using(Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile())
{
zipFile.AddEntry("Report.xml", (name,stream) => dt.WriteXml(stream) );
zipFile.Save(Response.OutputStream);
}
Isso grava o conjunto de dados diretamente no fluxo compactado no ZipFile. Muito eficiente! Sem buffer duplo. O delegado anônimo é chamado no momento do zipfile.save (). Apenas uma gravação (+compressa) é realizada.
Outras dicas
Por que você não fechou o MemoryStream, eu envolveria isso em um using
Cláusula, o mesmo poderia ser dito para zipFile
? Também dt
Presumo que seja um datatable ... coloque a verificação de erros para ver se há linhas, veja o código abaixo ...
dt.TableName = "Declaration"; if (dt.Rows != null && dt.Rows.Count >= 1){ using (MemoryStream stream = new MemoryStream()){ dt.WriteXml(stream); // Thanks Cheeso/Mikael stream.Seek(0, SeekOrigin.Begin); // using (ZipFile zipFile = new ZipFile()){ zipFile.AddEntry("Report.xml", "", stream); Response.ClearContent(); Response.ClearHeaders(); Response.AppendHeader("content-disposition", "attachment; filename=Report.zip"); //zipFile.Save(Response.OutputStream); zipFile.Save(stream); // Commented this out /* Response.Write(zipstream); // <----- Where did that come from? */ } Response.Write(stream); } } // No rows...don't bother...
Editar: Tendo olhado para isso de novo, e percebendo que Ionic.ziplib Do codePlex foi usado, mudei um pouco o código, em vez de zipFile.Save(Response.OutputStream);
eu usei zipFile.Save(stream);
usando o stream
instância do MemoryStream
aula e escreva usando Response.Write(stream);
.
Editar#2: Graças a Cheeso + Mikael Por apontar a falha óbvia - eu perdi uma milha de distância e não entendi o comentário deles até perceber que o fluxo estava no final ...
Você já tentou lavar o fluxo antes de fechar?
dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
OK. Não parece que estamos chegando muito longe aqui, então você precisa começar a depurar um pouco mais.
Atualize seu código para fazer o seguinte:
dt.WriteXml(stream);
stream.Seek(0, SeekOrigin.Begin);
File.WriteAllBytes("c:\test.xml", stream.GetBuffer());
Veja se você possui um arquivo XML válido. Se você seguir em frente, faça o mesmo com o seu ZipFile. Salve -o em um arquivo local. Veja se está lá, possui seu arquivo XML e seu arquivo XML possui conteúdo.
Se isso funcionar, tente enviar de volta apenas o fluxo de memória como resposta, veja se isso funciona.
Você deve poder rastrear ainda mais o problema.
Adicione um cabeçalho de contentType:
Response.ContentType = "application/zip";
Isso permitirá que os navegadores detectem o que você está enviando.
Verifique duas vezes o fluxo que você está retornando também. No seu exemplo abaixo
zipFile.Save(Response.OutputStream);
Response.Write(zipstream);
zipFile.Dispose();
Você está salvando o ZipFile no seu fluxo de resposta usando o método SALVE, mas também está chamando a resposta.Write () com uma variável ZipStream. O que é ZipStream? Verifique se também não é um fluxo vazio.
Criando um arquivo zip a partir do fluxo e baixando -o. Abaixo está o código.
FileStream stream=File.OpenRead(@"D:\FileDownLoad\DeskTop\1.txt");
MemoryStream MS=new MemoryStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(MS);
zipOutputStream.SetLevel(9);
ZipEntry entry = new ZipEntry("1.txt");
zipOutputStream.PutNextEntry(entry);
byte[] buffer = new byte[stream.Length];
int byteRead = 0;
while ((byteRead = stream.Read(buffer, 0, buffer.Length)) > 0)
zipOutputStream.Write(buffer, 0, byteRead);
zipOutputStream.IsStreamOwner = false;
stream.Close();
zipOutputStream.Close();
MS.Position = 0;
Response.ContentType = "application/application/octet-stream";
Response.AppendHeader("content-disposition", "attachment; filename=\"Download.zip\"");
Response.BinaryWrite(MS.ToArray());
Este código o ajudará a baixar um arquivo do Stream.
using (var outStream = new MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry("FileName.pdf", CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (WebResponse response = req.GetResponse())
{
using (var fileToCompressStream = response.GetResponseStream())
{
fileToCompressStream.CopyTo(entryStream);
}
}
}
using (var fileStream = new FileStream(@"D:\test.zip", FileMode.Create))
{
outStream.Seek(0, SeekOrigin.Begin);
outStream.CopyTo(fileStream);
}
}
Namespaces necessários:
using System.IO.Compression;
using System.IO.Compression.ZipArchive;