2012-10-24 4 views
2

나는 Asp.Net Mvc 응용 프로그램이 있습니다. 이 응용 프로그램에서는 데이터베이스에서 Excel 파일 (OpenXml SDK 포함)로 데이터를 다운로드 할 수있는 기능이 있습니다. 이제 작동합니다. 그러나 데이터가 크면 사용자 요청에서 다운로드 창이있는 응답 시간은 10 분 이상이됩니다. 이는 두 가지 긴 프로세스 때문입니다.OpenXml 및 HttpResponse.OutputStream

  1. MSSQL 서버에서 데이터를 가져 오는 중입니다.
  2. 서버의 메모리에 Excel 문서 생성. (Excel 문서가 완료된 경우에만 다운로드가 시작됩니다.)

첫 번째 문제는 DataReader를 사용하여 해결되었습니다. 이제 엑셀 파일 생성은 사용자 요청이 웹 서버에 도착한 직후에 시작됩니다.

두 번째 문제를 해결하려면 HttpResponse.OutputStream에서 Excel 문서를 생성해야하지만이 스트림은 Seekable이 아니며 시작하기 전에 생성이 실패합니다.

누구나이 문제를 해결하는 데 도움이되는 해결 방법을 알고 있습니까? 내 생성 기능의

샘플 : (HttpResponse.OutputStream 작업을위한)

public void GenerateSpreadSheetToStream(IDataReader dataReader, Stream outputStream) 
    { 
     var columnCaptions =    FillColumnCaptionsFromDataReader(dataReader.GetSchemaTable()); 
//fails on next line with exception "Cannot open package because FileMode or FileAccess value is not valid for the stream." 
     using (var spreadsheetDocument = SpreadsheetDocument.Create(outputStream, SpreadsheetDocumentType.Workbook)) 
     { 
      spreadsheetDocument.AddWorkbookPart(); 
      var workSheetPart = spreadsheetDocument.WorkbookPart.AddNewPart<WorksheetPart>(); 
      OpenXmlWriter writer; 
      using (writer = OpenXmlWriter.Create(workSheetPart)) 
      { 
       using (writer.Write(new Worksheet())) 
       { 

        using (writer.Write(new SheetData())) 
        { 
         using (writer.Write(w => 
          w.WriteStartElement(new Row(), new[] {new OpenXmlAttribute("r", null, 1.ToString(CultureInfo.InvariantCulture))}))) 
         { 
          var cells = 
           columnCaptions.Select(caption => new Cell() 
            { 
             CellValue = new CellValue(caption.Item2), 
             DataType = CellValues.String 
            }); 
          foreach (var cell in cells) 
          { 
           writer.WriteElement(cell); 
          } 
         } 
         var i = 2; 
         while (dataReader.Read()) 
         { 
          var oxa = new[] { new OpenXmlAttribute("r", null, i.ToString(CultureInfo.InvariantCulture)) }; 
          using (writer.Write(w => w.WriteStartElement(new Row(), oxa))) 
          { 
           var cells = 
            columnCaptions.Select(
             (c, j) => 
             new Cell 
              { 
               CellValue = new CellValue(dataReader[c.Item1].ToString()), 
               DataType = CellValues.String, 
               CellReference = new StringValue(GetSymbolByCellNumber(j)) 
              }); 
           foreach (var cell in cells) 
           { 
            writer.WriteElement(cell); 
           } 
          } 
          i++; 
         } 
        } 
       } 
      } 
      using (writer = OpenXmlWriter.Create(spreadsheetDocument.WorkbookPart)) 
      { 
       using (writer.Write(new Workbook())) 
       { 
        using (writer.Write(new Sheets())) 
        { 
         var sheet = new Sheet 
         { 
          Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(workSheetPart), 
          SheetId = 1, 
          Name = SheetName 
         }; 
         writer.WriteElement(sheet); 
        } 
       } 
      } 
     } 
    } 
    private static string GetSymbolByCellNumber(int number) 
    { 
     var r = number/26; 
     var s = (char) ((number%26) + 65); 
     return new string(s, r); 
    } 

내 FileStreamResultWithTransformation :

public class FileStreamResultWithTransformation : FileResult 
{ 
    private readonly Action<Stream> _action; 
    public FileStreamResultWithTransformation(Action<Stream> action, string contentType, string fileName) : base(contentType) 
    { 
     _action = action; 
     FileDownloadName = fileName; 
    } 

    protected override void WriteFile(HttpResponseBase response) 
    { 
     response.BufferOutput = false; 
     _action(response.OutputStream); ->> it fails there 
    } 
} 

스택 트레이스 :

[IOException가 : 열 수 없습니다 패키지 때문에하는 FileMode 또는 FileAccess 값이 스트림에 유효하지 않습니다.] System.IO.Packaging.Package.Val idetModeAndAccess (스트림 s, FileMode 모드, FileAccess 액세스) +784533 System.IO.Packaging.Package.Open (스트림 스트림, FileMode packageMode, FileAccess packageAccess, 부울 스트리밍) +89 System.IO.Packaging.Package.Open 스트림 packageMode하는 FileMode, FileAccess packageAccess) +10 DocumentFormat.OpenXml.Packaging.OpenXmlPackage.CreateCore (스트림 스트림) 192 DocumentFormat.OpenXml.Packaging.SpreadsheetDocument.Create (스트림 스트림 SpreadsheetDocumentType 유형 부울 자동 저장) 215 DOCUMENTFORMAT .OpenXml.Packaging.SpreadsheetDocument.Create (Stream stream, SpreadsheetDocumentType type) +44 d : \ Work \ Epsilon \ development \ Web \ trunk \ Sources \에있는 GenerateSpreadSheetToStream (IDataReader dataReader, Stream outputStream) Epsilon.DocumentGenerator \ XlsXGenerator.cs : 119

+0

나는 얼마 전에 일하게하려고 노력했다. 나는 그 당시의 길을 찾지 못했고'MemoryStream'과'.WriteTo (response.OutputStream)'을 고수해야했다. – tpeczek

+0

이것은 내 두 번째 문제를 해결하지 못합니다 (Excel 문서가 완료된 경우에만 다운로드가 시작됩니다). –

+0

나는 이것이 대답이 아니라, 단지 의견이라는 것을 알고있다. – tpeczek

답변

1

이 문제는 해결할 수 없다고 생각됩니다. 작성 완료시, OpenXmlWriter는 스트림의 다른 위치를 찾고, 읽고 쓰며,이 작업을하지 않으면 xlsx 파일이 손상됩니다. OpenXml 라이브러리의 디자인에 문제가 있다고 생각합니다.

0

문제가 조금 더 깊습니다. Xlsx 파일은 zip 아카이브이며 OpenXml은 내부적으로 ZipArchive 클래스를 사용합니다. 아카이브의 각 파일에는 헤더가 있으며 데이터 앞에 위치합니다. ZipArchive는 데이터를 스트림에 쓴 다음 파일의 시작 부분으로 돌아가 파일의 헤더를 씁니다. 그것은 Stream.Seek 메서드를 사용하고 HttpResponse.OutputStream은 이런 식으로 작동하지 않습니다.

+0

모르는 경우 파일의 시작 부분으로 돌아 가지 않고 스트리밍 방식으로 zip 형식으로 보관할 수 있습니다 (SharpZipLib 예제와 같이 많은 라이브러리가 있음). 그러나 OpenXml은 이것을 사용하지 않습니다. 그리고 이것은 OpenXml 라이브러리의 디자인 오류입니다. –