2009-07-02 13 views
10

次のコードを使用しようとしています:破損したzipファイルがあります。どうして? ファイル名はOKと思われます。おそらく彼らは相対的な名前ではないでしょう、それは問題ですか?リアルタイムでResponse.Outputにストリームしてストリームする方法を教えてください。

 private void trySharpZipLib(ArrayList filesToInclude) 
    { 
     // Response header 
     Response.Clear(); 
     Response.ClearHeaders(); 
     Response.Cache.SetCacheability(HttpCacheability.NoCache); 
     Response.StatusCode = 200; // http://community.icsharpcode.net/forums/p/6946/20138.aspx 
     long zipSize = calculateZipSize(filesToInclude); 
     string contentValue = 
      string.Format("attachment; filename=moshe.zip;" 
         ); // + " size={0}", zipSize); 
     Response.ContentType = "application/octet-stream"; //"application/zip"; 
     Response.AddHeader("Content-Disposition", contentValue); 
     Response.Flush(); 

     using (ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream)) 
     { 
      zipOutputStream.SetLevel(0); 

      foreach (string f in filesToInclude) 
      { 
       string filename = Path.Combine(Server.MapPath("."), f); 
       using (FileStream fs = File.OpenRead(filename)) 
       { 
        ZipEntry entry = 
         new ZipEntry(ZipEntry.CleanName(filename)) 
          { 
           DateTime = File.GetCreationTime(filename), 
           CompressionMethod = CompressionMethod.Stored, 
           Size = fs.Length 
          }; 
        zipOutputStream.PutNextEntry(entry); 

        byte[] buffer = new byte[fs.Length]; 
        // write to zipoutStream via buffer. 
        // The zipoutStream is directly connected to Response.Output (in the constructor) 
        ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fs, zipOutputStream, buffer); 
        Response.Flush(); // for immediate response to user 
       } // .. using file stream 
      }// .. each file 
     } 
     Response.Flush(); 
     Response.End(); 
    } 

答えて

0

次のヘッダーを追加してください。

Response.AddHeader("Content-Length", zipSize); 

私が以前に問題を引き起こしていたことは知っています。

編集:

これらの他の2が同様に役立つことがあります。

Response.AddHeader("Content-Description", "File Transfer"); 
Response.AddHeader("Content-Transfer-Encoding", "binary"); 
0

は、応答をフラッシュする前にZipOutputStreamをフラッシュしようとしたことがありますか? zipをクライアントに保存し、zipユーティリティでテストできますか?

2

ASP.NETでこれを行う方法(以前は試していませんが)は一般的にHTTPクライアントがHTTPバージョン1.1をサポートしている場合(リクエストのバージョンで示されているように)、サーバーは「チャンク」を指定する「Transfer-Encoding」レスポンスヘッダーを送信し、複数のデータブロックが使用可能になるとレスポンスデータを送信します。これにより、事前に最終的なデータサイズがわからない(したがって、Content-Lengthレスポンスヘッダを設定できない)データのリアルタイムストリーミングが可能になります。詳細はRFC 2616セクション3.6をご覧ください。

+0

ASP.NETでは、あなたは偽Response.BufferOutput =を設定することによってこれを行います。 – Cheeso

14

男の子、それはたくさんのコードです!あなたの仕事はDotNetZipを使って簡単になります。 )あなたがしたい場合は(そしてAddFiles前に、zipファイルをパスワードで暗号化する

Response.Clear(); 
Response.BufferOutput = false; 
string archiveName= String.Format("archive-{0}.zip", DateTime.Now.ToString("yyyy-MMM-dd-HHmmss")); 
Response.ContentType = "application/zip"; 
// see http://support.microsoft.com/kb/260519 
Response.AddHeader("content-disposition", "attachment; filename=" + archiveName); 
using (ZipFile zip = new ZipFile()) 
{ 
    // filesToInclude is a IEnumerable<String> (String[] or List<String> etc) 
    zip.AddFiles(filesToInclude, "files"); 
    zip.Save(Response.OutputStream); 
} 
// Response.End(); // will throw an exception internally. 
// Response.Close(); // Results in 'Failed - Network error' in Chrome. 
Response.Flush(); // See https://stackoverflow.com/a/736462/481207 
// ...more code here... 

、これらの行を挿入::この作品、HTTP 1.1クライアントと仮定すると、あなたが自己をしたい場合

zip.Password = tbPassword.Text; // optional 
    zip.Encryption = EncryptionAlgorithm.WinZipAes256; // optional 

をアーカイブを抽出し、zip.SaveSelfExtractor()でzip.Save()を置き換えます。


補遺; some people have commented to me DotNetZipは、ストリーミングする前にZIP全体をメモリに作成するため、DotNetZipが「いいえ」であることを示します。これは当てはまりません。 AddFilesを呼び出すと、ライブラリはエントリのリストを作成します - オブジェクトは、ジップアップされるものの状態を表します。保存の呼び出しまでは圧縮も暗号化も行われません。 Save()コールへのストリームを指定すると、すべての圧縮バイトがクライアントに直接ストリーミングされます。

SharpZipLibモデルでは、エントリを作成してからストリーミングし、次に別のエントリを作成してストリームなどにすることができます。 DotNetZipを使用すると、アプリケーションはまずエントリの完全なリストを作成し、それらをすべてストリームします。いずれのアプローチも、他のファイルよりも「高速」ではありませんが、長いファイルリストの場合、30,000と言えば、SharpZipLibでは最初のバイトまでの時間が短くなります。一方、30,000エントリのzipファイルを動的に作成することはお勧めしません。 DotNetZip v1.9デベロッパーのよう


EDIT

、DotNetZipは、同様にZipOutputStreamをサポートしています。私はまだ、私がここに示したように物事を行う方が簡単だと思います。


一部の人々は、彼らがすべてのユーザーに対して「ほとんど同じ」であるジップコンテンツを持っている場合がありますが、それぞれに対して異なるファイルのカップルがあります。 DotNetZipもこれで良いです。 zipアーカイブをファイルシステムファイルから読み込み、いくつかのエントリを更新したり(いくつか追加したり削除したりするなど)、Response.OutputStreamに保存することができます。この場合、DotNetZipは変更していないエントリを再圧縮または再暗号化しません。はるかに高速。

もちろん、DotNetZipはASP.NETだけでなく、あらゆる.NETアプリケーション用です。だからあなたはどんなストリームにも保存することができます。

さらに詳しい情報が必要な場合は、siteをチェックするか、dotnetzip forumsに投稿してください。

+0

ああ、あなた自身のライブラリを接続しています。 :)確かに、それは少しシンプルに見えます。 – Noldorin

+0

それを試してみてください、あなたはそれを愛するでしょう! – Cheeso

+1

ICSharpCode SharpZipLibを使用していましたが、DotNetZipに置き換えました。 DotNetZipインターフェイスは、SharpZipLibよりも簡単です。私たちはコードの半分以下で終了しました。私は今、大きなファンになっています。 Zipライブラリが必要な場合は、SharpZipを使用しないでください.DotNetZipを使用してください。 –

1

SharpZipLibのZipOutputStreamを見逃す人のために、DotNetZipを「通常の.NETストリーム方法」で使用できるようにする簡単なコードを示します。

しかし実際にはDotNetZip.Save()関数を呼び出す前に内部のMemoryStreamを使用するため、SharpZipLibのような実際のオンザフライストリーミングソリューションに比べて非効率的であることに注意してください。しかし残念ながら、SharpZibLibはまだEAS暗号化を扱っていません。 CheesoがいつでもdotNetZipでこの機能を追加することを願っていますか? ;-)

/// <summary> 
/// DotNetZip does not support streaming out-of-the-box up to version v1.8. 
/// This wrapper class helps doing so but unfortunately it has to use 
/// a temporary memory buffer internally which is quite inefficient 
/// (for instance, compared with the ZipOutputStream of SharpZibLib which has other drawbacks besides). 
/// </summary> 
public class DotNetZipOutputStream : Stream 
{ 
    public ZipFile ZipFile { get; private set; } 

    private MemoryStream memStream = new MemoryStream(); 
    private String nextEntry = null; 
    private Stream outputStream = null; 
    private bool closed = false; 

    public DotNetZipOutputStream(Stream baseOutputStream) 
    { 
     ZipFile = new ZipFile(); 
     outputStream = baseOutputStream; 
    } 

    public void PutNextEntry(String fileName) 
    { 
     memStream = new MemoryStream(); 
     nextEntry = fileName; 
    } 

    public override bool CanRead { get { return false; } } 
    public override bool CanSeek { get { return false; } } 
    public override bool CanWrite { get { return true; } } 
    public override long Length { get { return memStream.Length; } } 
    public override long Position 
    { 
     get { return memStream.Position; } 
     set { memStream.Position = value; } 
    } 

    public override void Close() 
    { 
     if (closed) return; 

     memStream.Position = 0; 
     ZipFile.AddEntry(nextEntry, Path.GetDirectoryName(nextEntry), memStream); 
     ZipFile.Save(outputStream); 
     memStream.Close(); 
     closed = true; 
    } 

    public override void Flush() 
    { 
     memStream.Flush(); 
    } 

    public override int Read(byte[] buffer, int offset, int count) 
    { 
     throw new NotSupportedException("Read"); 
    } 

    public override long Seek(long offset, SeekOrigin origin) 
    { 
     throw new NotSupportedException("Seek"); 
    } 

    public override void SetLength(long value) 
    { 
     memStream.SetLength(value); 
    } 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
     memStream.Write(buffer, offset, count); 
    } 
} 
+1

DotNetZip v1.9では、出力ストリームに直接書き込むことができます。 AddEntry(文字列、WriteDelegate)を使用します。匿名メソッドを使用してXMLをDataSetからzipファイルに埋め込む例: zip.AddEntry(zipEntryName、(name、stream)=> dataset1.WriteXml(stream)); – Cheeso

0

Necromancing。ここ
は、それがコメントでCheesoによって推奨されているようDotNetZip v1.9デベロッパーの+で始まる、クロージャを使用して、適切に行うの方法は次のとおりです。

public static void Run() 
{ 
    using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile()) 
    { 

     for (int i = 1; i < 11; ++i) 
     { 
      zip.AddEntry("LeaseContractForm_" + i.ToString() + ".xlsx", delegate(string filename, System.IO.Stream output) 
      { 
       // ByteArray from ExecuteReport - only ONE ByteArray at a time, because i might be > 100, and ba.size might be > 20 MB 
       byte[] ba = Portal_Reports.LeaseContractFormPostProcessing.ProcessWorkbook(); 
       output.Write(ba, 0, ba.Length); 
      }); 
     } // Next i 

     using (System.IO.Stream someStream = new System.IO.FileStream(@"D:\test.zip", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) 
     { 
      zip.Save(someStream); 
     } 
    } // End Using zip 

} // End Sub Run 

とVB.NETのバリアント、ケースには誰もがそれを必要とする(つまり、この点に注意してください単なるテストである。現実には、ループの各ステップごとに異なるin_contract_uidとin_premise_uidと呼ばれます):

Imports System.Web 
Imports System.Web.Services 


Public Class test 
    Implements System.Web.IHttpHandler 


    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest 
     Dim in_contract_uid As String = context.Request.Params("in_contract_uid") 
     Dim in_premise_uid As String = context.Request.Params("in_premise_uid") 

     If String.IsNullOrWhiteSpace(in_contract_uid) Then 
      in_contract_uid = "D57A62D7-0FEB-4FAF-BB09-84106E3E15E9" 
     End If 

     If String.IsNullOrWhiteSpace(in_premise_uid) Then 
      in_premise_uid = "165ECACA-04E6-4DF4-B7A9-5906F16653E0" 
     End If 

     Dim in_multiple As String = context.Request.Params("in_multiple") 
     Dim bMultiple As Boolean = False 

     Boolean.TryParse(in_multiple, bMultiple) 


     If bMultiple Then 
      Using zipFile As New Ionic.Zip.ZipFile 

       For i As Integer = 1 To 10 Step 1 
        ' Dim ba As Byte() = Portal_Reports.LeaseContractFormReport.GetLeaseContract(in_contract_uid, in_premise_uid) ' 
        ' zipFile.AddEntry("LeaseContractForm_" + i.ToString() + ".xlsx", ba) ' 

        zipFile.AddEntry("LeaseContractForm_" + i.ToString() + ".xlsx", Sub(filename As String, output As System.IO.Stream) 
                         Dim ba As Byte() = Portal_Reports.LeaseContractFormReport _ 
                         .GetLeaseContract(in_contract_uid, in_premise_uid) 
                         output.Write(ba, 0, ba.Length) 
                        End Sub) 
       Next i 

       context.Response.ClearContent() 
       context.Response.ClearHeaders() 
       context.Response.ContentType = "application/zip" 
       context.Response.AppendHeader("content-disposition", "attachment; filename=LeaseContractForm.zip") 
       zipFile.Save(context.Response.OutputStream) 
       context.Response.Flush() 
      End Using ' zipFile ' 
     Else 
      Dim ba As Byte() = Portal_Reports.LeaseContractFormReport.GetLeaseContract(in_contract_uid, in_premise_uid) 
      Portal.ASP.NET.DownloadFile("LeaseContractForm.xlsx", "attachment", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ba) 
     End If 

    End Sub 


    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable 
     Get 
      Return False 
     End Get 
    End Property 


End Class