2016-11-29 3 views
0

Wenn ein Benutzer auf eine bestimmte Schaltfläche klickt, müssen einige Dateien in einer ZIP-Datei archiviert und versendet werden. Die Dateien selbst werden von einem dritten Teil generiert und ich habe nur die URLs. Ich habe das teilweise geschafft, aber ich habe einige Probleme.Eine Zip-Datei senden, während sie erstellt wird

Erstens, wenn es viele Dateien zu zippen gibt, ist die Serverantwort langsam, da sie zuerst die Zip-Datei erstellt und dann sendet. Es kann sogar nach einer Weile abstürzen (insbesondere bekomme ich den Fehler "Überlauf oder Unterlauf in der arithmetischen Operation.").

Zweitens, jetzt wird die Datei nur gesendet, sobald das Zip-Archiv abgeschlossen ist. Ich möchte, dass der Download sofort beginnt. Sobald der Benutzer auf "Speichern" aus dem Dialog geklickt hat, beginnen die Daten zu senden und senden weiter, während die Zip-Datei "on the fly" erstellt wird. Ich habe diese Funktion auf einigen Websites gesehen, zum Beispiel: http://download.muuto.com/

Problem ist, ich kann nicht herausfinden, wie das geht.

Ich habe Teile des Codes von dieser Frage verwendet: Creating a dynamic zip of a bunch of URLs on the fly Und aus diesem Blog-Eintrag: http://dejanstojanovic.net/aspnet/2015/march/generate-zip-file-on-the-fly-in-aspnet-mvc-application/

Die Zip-Datei selbst erstellt und zurückgegeben in einer ASP.NET MVC-Controller-Methode. Hier ist mein Code:

using ICSharpCode.SharpZipLib.Zip; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Web; 
using System.Web.Mvc; 

namespace MyProject.Controllers 
{ 
    public class MyController : Controller 
    {   
     public ActionResult DownloadFiles() 
     { 
      var files = SomeFunction(); 

      byte[] buffer = new byte[4096]; 

      var baseOutputStream = new MemoryStream(); 
      ZipOutputStream zipOutputStream = new ZipOutputStream(baseOutputStream); 
      zipOutputStream.SetLevel(0); //0-9, 9 being the highest level of compression 
      zipOutputStream.UseZip64 = UseZip64.Off; 
      zipOutputStream.IsStreamOwner = false; 

      foreach (var file in files) 
      { 
       using (WebClient wc = new WebClient()) 
       { 
        // We open the download stream of the file 
        using (Stream wcStream = wc.OpenRead(file.Url)) 
        { 
         ZipEntry entry = new ZipEntry(ZipEntry.CleanName(file.FileName)); 
         zipOutputStream.PutNextEntry(entry); 

         // As we read the stream, we add its content to the new zip entry 
         int count = wcStream.Read(buffer, 0, buffer.Length); 
         while (count > 0) 
         { 
          zipOutputStream.Write(buffer, 0, count); 
          count = wcStream.Read(buffer, 0, buffer.Length); 
          if (!Response.IsClientConnected) 
          { 
           break; 
          } 
         } 
        } 
       } 
      } 
      zipOutputStream.Finish(); 
      zipOutputStream.Close(); 

      // Set position to 0 so that cient start reading of the stream from the begining 
      baseOutputStream.Position = 0; 

      // Set custom headers to force browser to download the file instad of trying to open it 
      return new FileStreamResult(baseOutputStream, "application/x-zip-compressed") 
      { 
       FileDownloadName = "Archive.zip" 
      }; 
     } 
    } 
} 
+0

Mögliches Duplikat [in eine Zip-Datei über HTTP-Streaming. net mit SharpZipLib] (http://stackoverflow.com/questions/626196/streaming-a-zip-file-over-http-in-net-with-sharpziplib) –

+0

Definitiv verwandt (dasselbe Ziel), aber nicht doppelt: I Ich versuche es von einem MVC-Controller zu machen, was ein anderer Ansatz ist. Ich habe versucht, 'BufferOutput = false' auf verschiedene Arten zu verwenden, aber es scheint sich nicht viel zu ändern. – Ogier

+0

Es ist nicht anders. Sie können ein 'FileResult' zurückgeben, das einen Stream als Parameter akzeptiert –

Antwort

1

Ok durch ein wenig mit der Antwort Output und Pufferung Hantieren habe ich zu einer Lösung gekommen:

using ICSharpCode.SharpZipLib.Zip; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Web; 
using System.Web.Mvc; 

namespace MyProject.Controllers 
{ 
    public class MyController : Controller 
    {   
     public ActionResult DownloadFiles() 
     { 
      var files = SomeFunction(); 

      // Disable Buffer Output to start the download immediately 
      Response.BufferOutput = false; 

      // Set custom headers to force browser to download the file instad of trying to open it 
      Response.ContentType = "application/x-zip-compressed"; 
      Response.AppendHeader("content-disposition", "attachment; filename=Archive.zip"); 

      byte[] buffer = new byte[4096]; 

      ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream); 
      zipOutputStream.SetLevel(0); // No compression 
      zipOutputStream.UseZip64 = UseZip64.Off; 
      zipOutputStream.IsStreamOwner = false; 

      try 
      { 
       foreach (var file in files) 
       { 
        using (WebClient wc = new WebClient()) 
        { 
         // We open the download stream of the image 
         using (Stream wcStream = wc.OpenRead(file.Url)) 
         { 
          ZipEntry entry = new ZipEntry(ZipEntry.CleanName(file.FileName)); 
          zipOutputStream.PutNextEntry(entry); 

          // As we read the stream, we add its content to the new zip entry 
          int count = wcStream.Read(buffer, 0, buffer.Length); 
          while (count > 0) 
          { 
           zipOutputStream.Write(buffer, 0, count); 
           count = wcStream.Read(buffer, 0, buffer.Length); 
           if (!Response.IsClientConnected) 
           { 
            break; 
           } 
          } 
         } 
        } 
       } 
      } 
      finally 
      { 
       zipOutputStream.Finish(); 
       zipOutputStream.Close(); 
      } 

      return new HttpStatusCodeResult(HttpStatusCode.OK); 
     } 
    } 
} 
Verwandte Themen