web-dev-qa-db-ja.com

ストリームからZipファイルを作成してダウンロードする

DotNetZipを使用して、xmlに変換してからZipするDataTableがあります。最後に、ユーザーはAsp.Net Webページからダウンロードできます。以下の私のコード

    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();

zipファイルのxmlファイルは空です。

45
Navid Farhadi

2つのこと。まず、持っているコード設計を保持している場合、エントリに書き込む前にMemoryStreamでSeek()を実行する必要があります。

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); 
}

この設計を維持する場合でも、Dispose()を呼び出す代わりに、すべての DotNetZipの例 で説明したように、using()句をお勧めします。 using()句は、障害が発生した場合の信頼性が高くなります。

疑問に思うかもしれませんが、AddEntry()を呼び出す前に、なぜMemoryStreamを探す必要があるのですかその理由は、AddEntry()は、位置が重要なストリームを渡す呼び出し元をサポートするように設計されているためです。その場合、呼び出し元は、ストリームの現在位置を使用して、ストリームからエントリデータを読み取る必要があります。 AddEntry()はそれをサポートします。したがって、AddEntry()を呼び出す前に、ストリーム内の位置を設定します。

しかし、より良いオプションは、コードを修正して WriteDelegateを受け入れるAddEntry()のオーバーロード を使用することです。データセットをZipファイルに追加するために特別に設計されました。元のコードは、データセットをメモリストリームに書き込み、ストリームをシークし、ストリームのコンテンツをZipに書き込みます。 WriteDelegateを使用すると、データを1回書き込むだけで高速かつ簡単になります。コードは次のようになります。

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); 
}

これにより、zipファイルの圧縮ストリームにデータセットが直接書き込まれます。非常に効率的です!ダブルバッファリングなし。匿名デリゲートは、ZipFile.Save()のときに呼び出されます。 1回の書き込み(+ compress)のみが実行されます。

69
Cheeso

なぜMemoryStreamを閉じなかったのですか、それをusing句でラップします。zipFileについても同じことが言えますか?また、dt DataTableであると仮定します...行があるかどうかを確認するためのエラーチェックで、次のコードを参照してください...

 dt.TableName = "宣言"; 
 
 if(dt.Rows!= null && dt.Rows.Count> = 1){
 using(MemoryStream stream = new MemoryStream()){
 dt.WriteXml(stream); 
 
 // Cheeso/Mikael 
 stream.Seek(0、SeekOrigin.Begin); 
 //

 (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); 
 
 //これをコメントアウト
 /*
 Response.Write(zipstream); // <-----それはどこから来たのですか?
 */
} 
 Response.Write(stream); 
} 
 } 
 //行なし...わざわざ... 

Edit:これをもう一度見て、Codeplexの Ionic.Ziplib が使用されていることに気付き、コードを少し変更しました。 zipFile.Save(Response.OutputStream);の代わりに、streamクラスのMemoryStreamインスタンスを使用してzipFile.Save(stream);を使用し、Response.Write(stream);を使用して書き出しました。

Edit#2:ありがとうCheeso+Mikael明らかな欠陥を指摘してくれた-私はそれを少し逃したそして、私はストリームが最後にあることに気づくまで彼らのコメントを理解しませんでした...

6
t0mm13b

圧縮する前にストリームをフラッシュしようとしましたか?

dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
1
jmservera

OK。ここまで進んでいるとは思えないので、もう少しデバッグを開始する必要があります。

コードを更新して、次のことを行います。

dt.WriteXml(stream);
stream.Seek(0, SeekOrigin.Begin);
File.WriteAllBytes("c:\test.xml", stream.GetBuffer());

有効なXMLファイルが出力されているかどうかを確認してください。その場合は、ZipFileで同じことを行います。ローカルファイルに保存します。そこにあるか、xmlファイルがあり、xmlファイルにコンテンツがあるかどうかを確認します。

それが機能する場合は、メモリストリームのみを応答として送信してみてください。それが機能するかどうかを確認してください。

その後、問題をさらに追跡できます。

1
Ian

返されるストリームも再確認してください。以下の例では

zipFile.Save(Response.OutputStream);
Response.Write(zipstream);
zipFile.Dispose();

Saveメソッドを使用してzipFileを応答ストリームに保存していますが、zipstream変数を使用してResponse.Write()を呼び出しています。 zipstreamとは何ですか?空のストリームでもないことを確認します。

0
Ian

このコードは、ストリームからファイルをダウンロードするのに役立ちます。

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);
    }
}

必要な名前空間:

using System.IO.Compression;
using System.IO.Compression.ZipArchive;
0
Skull

ContentTypeヘッダーを追加します。

Response.ContentType = "application/Zip";

これにより、ブラウザは送信内容を検出できます。

0
Mikael Svenson

ストリームからZipファイルを作成してダウンロードします。以下はコードです。

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());
0