14

次のコードを使用しようとしています: 破損した zip ファイルを取得します。なんで?ファイル名は問題ないようです。おそらくそれらは相対的な名前ではなく、それが問題なのでしょうか?

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

6 に答える 6

16

少年、それはたくさんのコードです!DotNetZipを使用すると、作業がより簡単になります。HTTP 1.1 クライアントを想定すると、これは機能します。

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...

zip をパスワードで暗号化する場合は、AddFiles() の前に次の行を挿入します。

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

自己解凍アーカイブが必要な場合は、zip.Save() を zip.SaveSelfExtractor() に置き換えます。


補遺; 一部の人々は、DotNetZip は、ストリーミングする前にメモリ内に ZIP 全体を作成するため、「良くない」とコメントしています。そうではありません。AddFilesを呼び出すと、ライブラリはエントリのリストを作成します。これは、圧縮されるものの状態を表すオブジェクトです。Save を呼び出すまで、圧縮や暗号化は行われません。Save() 呼び出しにストリームを指定すると、圧縮されたすべてのバイトがクライアントに直接ストリーミングされます。

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


編集

DotNetZip v1.9 の時点で、DotNetZip は ZipOutputStream もサポートしています。ただし、ここで示した方法で物事を行う方が簡単だと思います。


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

もちろん、DotNetZip は ASP.NET だけでなく、あらゆる .NET アプリに対応しています。したがって、任意のストリームに保存できます。

詳細については、サイトをチェックするか、 dotnetzip フォーラムに投稿してください。

于 2009-07-03T21:13:12.657 に答える
2

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

于 2009-07-02T21:49:40.650 に答える
1

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

ただし、実際に 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);
    }
}
于 2009-10-02T13:53:04.267 に答える
0

ネクロマンシング。
コメントで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
于 2016-06-28T15:05:34.610 に答える
0

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

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

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

編集:

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

Response.AddHeader("Content-Description", "File Transfer");
Response.AddHeader("Content-Transfer-Encoding", "binary");
于 2009-07-02T14:48:49.903 に答える
0

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

于 2009-07-02T15:08:23.983 に答える