哪行代码仍在使用该文件但未发布?

时间:2016-04-18 16:02:02

标签: c# excel

我正在使用此代码创建Excel文件并使用数据填充它:

using (ExcelPackage package = new ExcelPackage(fileInfo))
{
    ExcelWorksheet ws = package.Workbook.Worksheets.Add("Deltas");
    ExcelWorksheet ws2 = package.Workbook.Worksheets.Add("Images");
    ExcelWorksheet ws3 = package.Workbook.Worksheets.Add("Data Points");

    GenerateDataSheet(ws, true);
    GenerateDataSheet(ws3, false);

    // populate second worksheet with images
    var imagesLocations = SelectedSession.GetTests().Where(t => t.IsReference).Select(t => t.Location).OrderBy(t => t.DateCreated).ThenBy(t => t.Name).ToList();
    ws2.Column(2).Width = 58;

    for (int i = 0; i < imagesLocations.Count; i++)
    {
        ws2.Row(i + 1).Height = 305;

        ws2.Cells[i + 1, 1].Value = imagesLocations[i].Name;
        ws2.Cells[i + 1, 1].Style.VerticalAlignment = ExcelVerticalAlignment.Top;
        ws2.Cells[i + 1, 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;

        var imagePath = imagesLocations[i].Tests.FirstOrDefault(t => t.IsReference).ImagePath;
        if (File.Exists(imagePath))
        {
            var ImageToPutInReport = Image.FromFile(imagePath);
            var image = ws2.Drawings.AddPicture(imagesLocations[i].Name, ImageToPutInReport);
            image.SetSize(375, 375);
            image.SetPosition(i, 0, 1, 0);
        }
    }

    package.SaveAs(fileInfo);
}

完成后,我调用一个函数来删除图像文件夹。 “delete()”函数弹出错误:

  

图片仍在使用中

当我评论上述代码时,不会发生错误。目前我正在使用这个黑客来解决我的问题:

public static void DeleteSessionFolder(string session)
{
    try
    {
        if (Directory.Exists(baseSessionPath + session))
            Directory.Delete(baseSessionPath + session, true); // error pops here

    } catch (Exception e)
    {
        DeleteSessionFolder(session); // call it again
    }
}

所以我有机会一次又一次地尝试。但是这需要15秒,直到“那件事”发布图像并且应用程序能够删除文件夹,而整个应用程序被冻结。 哪一行代码保留图像(图像)?

3 个答案:

答案 0 :(得分:4)

您应该使用流来读取图像副本,然后使用此副本进行操作,而不是使用Image.FromFile来获取图像。 Image.FromFile的问题在于它通过引用打开您的图像文件,以便在您的应用程序停止使用它之前,其他任何操作都无法写入。它基本上是一个永不关闭的流,直到完全超出范围或您手动.Dispose()图像对象。

所以,改变这一行:

var ImageToPutInReport = Image.FromFile(imagePath);

到这一行:

Image ImageToPutInReport;
using (FileStream stream = File.OpenRead(imagePath))
{
    ImageToPutInReport = Image.FromStream(stream);
}

答案 1 :(得分:1)

Image.FromFile(string filename)上的this链接显示The file remains locked until the Image is disposed.并且您不会将图片放在任何位置。

这就是造成你问题的原因。

答案 2 :(得分:1)

这一行:

var ImageToPutInReport = Image.FromFile(imagePath);

保持你的图像被打开并锁定,直到你处理掉它(因为你没有明确地做,直到垃圾收集器决定发生这种情况:那些应该是你在重试时观察的15秒)。 ..所以我将该块更改为:

using(var ImageToPutInReport = Image.FromFile(imagePath))
{
  var image = ws2.Drawings.AddPicture(imagesLocations[i].Name, ImageToPutInReport);
  image.SetSize(375, 375);
  image.SetPosition(i, 0, 1, 0);
}

您需要确保将Image对象复制(并且未链接)到电子表格中,否则,您需要稍后处理它们,但应该就是这样,采取你所看到的GC行为。

PS:正如我上面提到的,并且正如@ScottChamberlain在评论中指出的那样,图像可能会被添加为参考(而不是副本),因此您需要处置集合中引用的图像。 如果是这种情况,我们可以通过创建副本来解锁文件(这应该释放文件),然后在我们完成我们的软件包之后再处理副本......像:

var imageList = new List<Image>();
using (ExcelPackage package = new ExcelPackage(fileInfo))
{
   /* ... */
         if (File.Exists(imagePath))
         {
            Image ImageToPutInReport;
            // Make a copy of the loaded image and dispose the original
            // so the file is freed
            using(var tempImage = Image.FromFile(imagePath))
               ImageToPutInReport = new Bitmap(tempImage);

            // Add to the list of images we'll dispose later 
            // after we're done
            imageList.Add(ImageToPutInReport);
            var image = ws2.Drawings.AddPicture(imagesLocations[i].Name, ImageToPutInReport);
            image.SetSize(375, 375);
            image.SetPosition(i, 0, 1, 0);
        }
   /* ... */
   package.SaveAs(fileInfo);
}
foreach(var img in imageList)
  img.Dispose();
imageList.Clear();