【问题标题】:MemoryStream Disposed after using Workbook.Save() method OpenXML使用 Workbook.Save() 方法 OpenXML 后处理的 MemoryStream
【发布时间】:2021-11-30 21:03:38
【问题描述】:

我已经搞砸了几个小时,但无济于事。我正在尝试复制一个 excel 文件,向其中添加一个新工作表,将文件放入 MemoryStream 中,然后返回流。

代码如下:

public Stream ProcessDocument()
{
        var resultStream = new MemoryStream();

        string sourcePath = "path\\to\\file";
        string destinationPath = "path\\to\\file";

        CopyFile(destinationPath, sourcePath);

        var copiedFile = SpreadsheetDocument.Open(destinationPath, true);
        var fileWithSheets = SpreadsheetDocument.Open("path\\to\\file", false);

        AddCopyOfSheet(fileWithSheets.WorkbookPart, copiedFile.WorkbookPart, "foo");

        using(var stream = new MemoryStream()){
                copiedFile.WorkbookPart.Workbook.Save(stream);
                stream.Position = 0;
                stream.CopyTo(resultsStream);
        }

        return resultsStream;
}

public void CopyFile(string outputFullFilePath, string inputFileFullPath)
{       
        int bufferSize = 1024 * 1024;

        using (var fileStream = new FileStream(outputFullFilePath, FileMode.OpenOrCreate))
        {
            var fs = new FileStream(inputFileFullPath, FileMode.Open, FileAccess.ReadWrite);
            fileStream.SetLength(fs.Length);
            int bytesRead = -1;
            byte[] bytes = new byte[bufferSize];

            while ((bytesRead = fs.Read(bytes, 0, bufferSize)) > 0)
            {
                fileStream.Write(bytes, 0, bytesRead);
            }

            fs.Close();
            fileStream.Close();
        }
    }

public static void AddCopyOfSheet(WorkbookPart sourceDocument, WorkbookPart destinationDocument, string sheetName)
{
        WorksheetPart sourceSheetPart = GetWorkSheetPart(sourceDocument, sheetName);
        destinationDocument.AddPart(sourceSheetPart);
}

public static WorksheetPart GetWorksheetPart(WorkbookPart workbookPart, string sheetName)
{
        string id = workbookPart.Workbook.Descendants<Sheet>().First(x => x.Name.Value.Contains(sheetName)).Id
        return (WorksheetPart)workbookPart.GetPartById(id);
}

问题似乎来自copiedFile.WorkbookPart.Workbook.Save(stream)

运行后,我收到一条错误消息,提示存在“System.ObjectDisposedException”类型的异常。文件复制良好,添加工作表似乎也可以正常工作。

这是我尝试过的:

  1. 使用不带流的 .Save() 作为参数。它什么都不做。
  2. 使用两个不同的流(因此代码中的 resultStream 卡顿)
  3. 使用纯 OpenXML 并将 WorkbookParts 直接复制到流中。用纯文本 excel 测试并且很好,但它破坏了所需的文件,因为它具有一些似乎不适用于 OpenXML 的高级格式。不过,如果有人知道我如何解决这个问题,我愿意重构。

我没有尝试过的:

  1. 创建副本的另一个副本并使用 SpreadsheetDocument.Create(stream, type) 方法。我觉得这会奏效,但它似乎是一个糟糕而缓慢的解决方案。
  2. 更新 OpenXML。我目前是 2.5。

非常感谢任何反馈或想法。谢谢!

PS:我的开发箱是气隙的,所以我不得不手写这段代码。如有错误请见谅。

【问题讨论】:

    标签: c# openxml openxml-sdk


    【解决方案1】:

    原来copiedFile.WorkbookPart.Workbook.Save(stream); 默认处理流。解决方法是创建一个 MemoryStream 类,使其重载其被处置的能力,如下所示:

    public class DisposeLockableMemoryStream : MemoryStream
    {
       public DisposeLockableMemoryStream(bool allowDispose)
       {
           AllowDispose = allowDispose;
       }
    
       public bool AllowDispose { get; set; }
    
       protected override void Dispose(bool disposing)
       {
           if (!AllowDispose)
               return;
    
           base.Dispose(disposing);
       }
    }
    

    您需要做的就是确保您stream.AllowDispose = true,然后在完成后将其丢弃。

    现在,这并没有真正修复我的代码,因为事实证明.Save() 只跟踪对文档所做的更改,而不是整个事情!!!。基本上,这个图书馆是热垃圾,我很遗憾注册这个故事开始。

    有关详细信息,请参阅post I made on r/csharp

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-29
      • 2012-03-09
      相关资源
      最近更新 更多