50

DotNetZipを使用して、それをxmlに変換してから圧縮したい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 ファイルが空です。

4

8 に答える 8

71

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 回の書き込み (+圧縮) のみが実行されます。

于 2010-02-15T17:32:47.277 に答える
6

なぜあなたはMemoryStreamを閉じなかったusingzipFileですか?またdt、私はDataTableであると推測しています...行があるかどうかを確認するためにエラーチェックを入れてください。以下のコードを参照してください...

    dt.TableName = "宣言";

    if (dt.Rows != null && dt.Rows.Count >= 1){
      using (MemoryStream ストリーム = new MemoryStream()){
         dt.WriteXml(ストリーム);

         // ありがとう Cheeso/Mikael
         stream.Seek(0, SeekOrigin.Begin);
         ///

         使用 (ZipFile zipFile = 新しい ZipFile()){
             zipFile.AddEntry("Report.xml", "", ストリーム);
             Response.ClearContent();
             Response.ClearHeaders();
             Response.AppendHeader("content-disposition", "attachment; filename=Report.zip");

             //zipFile.Save(Response.OutputStream);
             zipFile.Save(ストリーム);

             // これをコメントアウト
             /*
               Response.Write(zipstream); // <----- どこから来たの?
             */
          }
          Response.Write(ストリーム);
       }
    }
    // 行がありません...気にしないでください...

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

編集#2:明らかな欠陥を指摘してくれたCheeso + Mikaelに感謝します-1マイルも離れていて、ストリームが最後にあることに気付くまで、彼らのコメントを理解していませんでした...

于 2010-02-15T14:04:01.617 に答える
1

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

dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
于 2010-02-15T14:13:15.440 に答える
1

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

You're コードを更新して、次の操作を行います。

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

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

それが機能する場合は、メモリ ストリームだけを応答として送り返してみて、それが機能するかどうかを確認します。

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

于 2010-02-15T15:12:24.007 に答える
0

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

Response.ContentType = "application/zip";

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

于 2010-02-15T13:46:08.187 に答える
0

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

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

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

于 2010-02-15T14:21:13.960 に答える
0

ストリームから 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());
于 2016-09-04T17:17:43.067 に答える