title: Generate Excel xlsx directly to web response stream
body: <p>On ASP.NET WebForms application, I'm trying to compose MS Excel (xlsx) file on-the-fly and save it directly to Response's output stream with periodical flushing the stream. The problem is, that Excel reportings corrupted file error on generated output file even if final xlsx (zip) file is browsable and extractable without any problems. When the file is generated to filestream using exactly same method and saved to disk on server's side, then the output file is OK. Bellow is the code and complete sample app is available here: <a href="https://github.com/pavelsommer/zipapp" rel="nofollow noreferrer">https://github.com/pavelsommer/zipapp</a></p>
  <pre><code>// this produces &quot;corrupted&quot; xlsx
  protected void button1_Click(object sender, EventArgs e)
  {
      string fileName = @&quot;output.xlsx&quot;;
  
      Response
          .Clear();
  
      Response
          .ContentType = &quot;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet&quot;;
  
      Response
          .AppendHeader(&quot;content-disposition&quot;,
          String
          .Format(&quot;attachment; filename=\&quot;{0}\&quot;;filename*=utf-8''{1}&quot;,
          fileName,
          HttpUtility
          .UrlPathEncode(fileName)));
  
      Response
          .CacheControl = &quot;Private&quot;;
  
      Response
          .BufferOutput = false;
  
      ComposeOutput(Response
          .OutputStream);
  
      Response
          .End();
  }
  
  // this produces perfectly valid xlsx
  protected void button2_Click(object sender, EventArgs e)
  {
      string fileName = @&quot;output.xlsx&quot;;
  
      using (var outputStream = new FileStream(Server
          .MapPath(String
          .Format(&quot;~/App_Data/{0}&quot;,
          fileName)),
          FileMode
          .Create))
      {
          ComposeOutput(outputStream);
      }
  }
  
  protected void ComposeOutput(Stream outputStream)
  {
      using (ZipOutputStream zipStream = new ZipOutputStream(outputStream))
      {
          zipStream
              .IsStreamOwner = true;
  
          ZipEntry zipEntry = new ZipEntry(ZipEntry
              .CleanName(@&quot;xl\worksheets\sheet1.xml&quot;));
  
          zipStream
              .PutNextEntry(zipEntry);
  
          // write dynamic content to output xlsx
          XmlWriter xmlWriter = XmlWriter
              .Create(zipStream);
  
          var excelWriter = new OpenXmlWriter(xmlWriter);
  
          excelWriter
              .OpenWorksheet();
  
          for (int i = 0; i &lt; 1000; i++)
          {
              excelWriter
                  .OpenRow();
  
              excelWriter
                  .WriteString(String
                  .Format(&quot;Hi there {0}&quot;,
                  i));
  
              excelWriter
                  .CloseRow();
  
              zipStream
                  .Flush();
          }
  
          excelWriter
              .CloseWorksheet();
  
          xmlWriter
              .Close();
  
          // writes rest of xlsx structure ([Content_Types].xml, workbook.xml etc.)
          WriteFiles(zipStream,
              Server
              .MapPath(&quot;~/App_Data/Excel&quot;));
  
          zipStream
              .Finish();
  
          zipStream
              .Flush();
  
          zipStream
              .Close();
      }
  }
  
  void WriteFiles(ZipOutputStream zipStream, string path, params string[] exclude)
  {
      foreach (var filePath in Directory
          .GetFiles(path,
          &quot;*.*&quot;,
          SearchOption
          .AllDirectories))
      {
          var relativePath = filePath
              .Replace(String
              .Format(&quot;{0}\\&quot;,
              path),
              null);
  
          if (exclude
              .Contains(relativePath))
              continue;
  
          WriteFile(zipStream,
              relativePath,
              filePath);
      }
  }
  
  void WriteFile(ZipOutputStream zipStream, string entryPath, string filePath)
  {
      ZipEntry zipEntry = new ZipEntry(ZipEntry
          .CleanName(entryPath));
  
      zipStream
          .PutNextEntry(zipEntry);
  
      byte[] buffer = File
          .ReadAllBytes(filePath);
  
      zipStream
          .Write(buffer,
          0,
          buffer
          .Length);
  
      zipStream
          .CloseEntry();
  }
  </code></pre>
  <p>I'am pretty sure, that the source of problem is dynamic content part in combination with Response's output stream. Final xlsx/zip file is malformed with &quot;Malformed 64-bit uncompressed size&quot; message due to the its ZIPEntry after close wants to update ZIP header in stream but it is not possible, because it is already flushed. So, the question is, that there is a way to solve it with some &quot;special&quot; technique or it is impossible.</p>