2

Asp.Net Mvc アプリケーションがあります。このアプリケーションには、データベースから Excel ファイル (OpenXml Sdk を使用) にデータをダウンロードする機能があります。それは今動作します。ただし、データが大きい場合、ユーザー リクエストからダウンロード ウィンドウでの応答までの時間は 10 分以上になります。これは、次の 2 つの長いプロセスが原因です。

  1. MSSQL サーバーからデータを取得しています。
  2. サーバー上のメモリに Excel ドキュメントを生成しています。(ダウンロードはエクセル文書が完成した時点で開始されます)

最初の問題は、DataReader を使用して解決されました。これで、Web サーバーへのユーザー リクエストが発生した直後に、Excel ファイルの生成が開始されます。

2 番目の問題を解決するには、HttpResponse.OutputStream で Excel ドキュメントを生成する必要がありますが、このストリームは Seekable ではなく、生成を開始する前に失敗します。

この問題を解決するのに役立つ回避策を知っている人はいますか?

私の生成関数のサンプル:

    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 (HttpResponse.OutputStream を操作するため):

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 の値がストリームに対して有効でないため、パッケージを開けません

ストリーム ストリーム、FileMode packageMode、FileAccess packageAccess、ブール ストリーミング) +89
System.IO.Packaging.Package.Open(ストリーム ストリーム、FileMode packageMode、FileAccess packageAccess) +10
DocumentFormat.OpenXml.Packaging.OpenXmlPackage.CreateCore(ストリーム ストリーム) +192
DocumentFormat.OpenXml.Packaging.SpreadsheetDocument.Create(ストリーム ストリーム、SpreadsheetDocumentType タイプ、Boolean autoSave) +215
DocumentFormat.OpenXml.Packaging.SpreadsheetDocument.Create(ストリーム ストリーム、SpreadsheetDocumentType タイプ) +44
-------.GenerateSpreadSheetToStream(IDataReader dataReader, Stream outputStream) in d:\Work\Epsilon\development\Web\trunk\Sources\Epsilon.DocumentGenerator\XlsXGenerator.cs:119

4

2 に答える 2

1

この問題は解決できないように思えます。書き込みのファイナライズ時に、OpenXmlWriter はストリームのさまざまな位置でシーク、読み取り、書き込みを行います。このアクションがないと、xlsx ファイルが破損します。OpenXml ライブラリの設計に問題があると思います。

于 2012-10-25T08:10:44.230 に答える
1

問題はもう少し深いです。xlsx ファイルは zip アーカイブであり、OpenXml は内部的に ZipArchive クラスを使用します。アーカイブ内の各ファイルにはヘッダーがあり、データの前に配置されます。ZipArchive はデータをストリームに書き込み、ファイルの先頭に戻ってファイルのヘッダーを書き込みます。Stream.Seek メソッドを使用しており、HttpResponse.OutputStream はこの方法では機能しません。

于 2015-07-10T09:42:28.307 に答える