使用C1ZipFile后为什么不能删除此文件?

时间:2021-03-08 20:27:59

The following code gives me a System.IO.IOException with the message 'The process cannot access the file'.

下面的代码给了我一个System.IO.IOException,消息'进程无法访问该文件'。

private void UnPackLegacyStats()
{
  DirectoryInfo oDirectory;
  XmlDocument oStatsXml;

  //Get the directory
  oDirectory = new DirectoryInfo(msLegacyStatZipsPath);

  //Check if the directory exists
  if (oDirectory.Exists)
  {
    //Loop files
    foreach (FileInfo oFile in oDirectory.GetFiles())
    {
      //Check if file is a zip file
      if (C1ZipFile.IsZipFile(oFile.FullName))
      {
        //Open the zip file
        using (C1ZipFile oZipFile = new C1ZipFile(oFile.FullName, false))
        {
          //Check if the zip contains the stats
          if (oZipFile.Entries.Contains("Stats.xml"))
          {
            //Get the stats as a stream
            using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
            {
              //Load the stats as xml
              oStatsXml = new XmlDocument();
              oStatsXml.Load(oStatsStream);

              //Close the stream
              oStatsStream.Close();
            }

            //Loop hit elements
            foreach (XmlElement oHitElement in oStatsXml.SelectNodes("/*/hits"))
            {
              //Do stuff
            }                
          }

          //Close the file
          oZipFile.Close();
        }
      }

      //Delete the file
      oFile.Delete();
    }
  }
}

I am struggling to see where the file could still be locked. All objects that could be holding onto a handle to the file are in using blocks and are explicitly closed.

我很难看到文件仍然可以锁定的位置。可以保留到文件句柄的所有对象都在使用块并显式关闭。

Is it something to do with using FileInfo objects rather than the strings returned by the static GetFiles method?

是否与使用FileInfo对象而不是静态GetFiles方法返回的字符串有关?

Any ideas?

5 个解决方案

#1


I assume you're getting the error on the oFile.Delete call. I was able to reproduce this error. Interestingly, the error only occurs when the file is not a zip file. Is this the behavior you are seeing?

我假设您在oFile.Delete调用上遇到错误。我能够重现这个错误。有趣的是,只有当文件不是zip文件时才会出现错误。这是你看到的行为吗?

It appears that the C1ZipFile.IsZipFile call is not releasing the file when it's not a zip file. I was able to avoid this problem by using a FileStream instead of passing the file path as a string (the IsZipFile function accepts either).

看来,当它不是zip文件时,C1ZipFile.IsZipFile调用不会释放该文件。通过使用FileStream而不是将文件路径作为字符串传递(IsZipFile函数接受),我能够避免此问题。

So the following modification to your code seems to work:

因此,对您的代码进行以下修改似乎有效:

if (oDirectory.Exists)
{
    //Loop files
    foreach (FileInfo oFile in oDirectory.GetFiles())
    {
        using (FileStream oStream = new FileStream(oFile.FullName, FileMode.Open))
        {
            //Check if file is a zip file
            if (C1ZipFile.IsZipFile(oStream))
            {
            // ...
            }
        }
        //Delete the file
        oFile.Delete();
    }
}    

In response to the original question in the subject: I don't know if it's possible to know if a file can be deleted without attempting to delete it. You could always write a function that attempts to delete the file and catches the error if it can't and then returns a boolean indicating whether the delete was successful.

回答主题中的原始问题:我不知道是否可以在不尝试删除文件的情况下删除文件。您总是可以编写一个尝试删除文件的函数,如果不能,则捕获错误,然后返回一个指示删除是否成功的布尔值。

#2


I do not see problems in your code, everything look ok. To check is the problem lies in C1ZipFile I suggest you initialize zip from stream, instead of initialization from file, so you close stream explicitly:

我没有在你的代码中看到问题,一切看起来都不错。要检查问题在于C1ZipFile我建议你从流初始化zip,而不是从文件初始化,所以你明确地关闭流:

//Open the zip file
using (Stream ZipStream = oFile.OpenRead())
using (C1ZipFile oZipFile = new C1ZipFile(ZipStream, false))
{
    // ...

Several other suggestions:

其他一些建议:

  • You do not need to call Close() method, with using (...), remove them.
  • 您不需要调用Close()方法,使用(...)删除它们。

  • Move xml processing (Loop hit elements) outsize zip processing, i.e. after zip file closeing, so you keep file opened as least as possible.
  • 移动xml处理(循环命中元素)超大zip处理,即在zip文件关闭后,所以你尽可能地保持文件打开。

#3


I'm just guessing: are you sure that oZipFile.Close() is enough? Perhaps you have to call oZipFile.Dispose() or oZipFile.Finalize() to be sure it has actually released the resources.

我只是猜测:你确定oZipFile.Close()就够了吗?也许你必须调用oZipFile.Dispose()或oZipFile.Finalize()来确保它实际上已经释放了资源。

#4


More then Likely it's not being disposed, anytime you access something outside of managed code(streams, files, etc.) you MUST dispose of them. I learned the hard way with Asp.NET and Image files, it will fill up your memory, crash your server, etc.

更多可能它没有被处理,无论何时你访问托管代码之外的东西(流,文件等),你必须处理它们。我用Asp.NET和Image文件学到了很多东西,它会填满你的内存,崩溃你的服务器等等。

#5


In the interest of completeness I am posing my working code as the changes came from more than one source.

为了完整起见,我正在构建我的工作代码,因为更改来自多个来源。

private void UnPackLegacyStats()
{
  DirectoryInfo oDirectory;
  XmlDocument oStatsXml;

  //Get the directory
  oDirectory = new DirectoryInfo(msLegacyStatZipsPath);

  //Check if the directory exists
  if (oDirectory.Exists)
  {
    //Loop files
    foreach (FileInfo oFile in oDirectory.GetFiles())
    {
      //Set empty xml
      oStatsXml = null;

      //Load file into a stream
      using (Stream oFileStream = oFile.OpenRead())
      {
        //Check if file is a zip file
        if (C1ZipFile.IsZipFile(oFileStream))
        {
          //Open the zip file
          using (C1ZipFile oZipFile = new C1ZipFile(oFileStream, false))
          {
            //Check if the zip contains the stats
            if (oZipFile.Entries.Contains("Stats.xml"))
            {
              //Get the stats as a stream
              using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
              {
                //Load the stats as xml
                oStatsXml = new XmlDocument();
                oStatsXml.Load(oStatsStream);
              }
            }
          }
        }
      }

      //Check if we have stats
      if (oStatsXml != null)
      {
        //Process XML here
      }

      //Delete the file
      oFile.Delete();
    }
  }
}

The main lesson I learned from this is to manage file access in one place in the calling code rather than letting other components manage their own file access. This is most apropriate when you want to use the file again after the other component has finished it's task.

我从中学到的主要教训是在调用代码中的一个位置管理文件访问,而不是让其他组件管理自己的文件访问。当您希望在其他组件完成任务后再次使用该文件时,这是最合适的。

Although this takes a little more code you can clearly see where the stream is disposed (at the end of the using), compared to having to trust that a component has correctly disposed of the stream.

虽然这需要更多代码,但您可以清楚地看到流处理的位置(在使用结束时),而不必相信组件已正确处理流。

#1


I assume you're getting the error on the oFile.Delete call. I was able to reproduce this error. Interestingly, the error only occurs when the file is not a zip file. Is this the behavior you are seeing?

我假设您在oFile.Delete调用上遇到错误。我能够重现这个错误。有趣的是,只有当文件不是zip文件时才会出现错误。这是你看到的行为吗?

It appears that the C1ZipFile.IsZipFile call is not releasing the file when it's not a zip file. I was able to avoid this problem by using a FileStream instead of passing the file path as a string (the IsZipFile function accepts either).

看来,当它不是zip文件时,C1ZipFile.IsZipFile调用不会释放该文件。通过使用FileStream而不是将文件路径作为字符串传递(IsZipFile函数接受),我能够避免此问题。

So the following modification to your code seems to work:

因此,对您的代码进行以下修改似乎有效:

if (oDirectory.Exists)
{
    //Loop files
    foreach (FileInfo oFile in oDirectory.GetFiles())
    {
        using (FileStream oStream = new FileStream(oFile.FullName, FileMode.Open))
        {
            //Check if file is a zip file
            if (C1ZipFile.IsZipFile(oStream))
            {
            // ...
            }
        }
        //Delete the file
        oFile.Delete();
    }
}    

In response to the original question in the subject: I don't know if it's possible to know if a file can be deleted without attempting to delete it. You could always write a function that attempts to delete the file and catches the error if it can't and then returns a boolean indicating whether the delete was successful.

回答主题中的原始问题:我不知道是否可以在不尝试删除文件的情况下删除文件。您总是可以编写一个尝试删除文件的函数,如果不能,则捕获错误,然后返回一个指示删除是否成功的布尔值。

#2


I do not see problems in your code, everything look ok. To check is the problem lies in C1ZipFile I suggest you initialize zip from stream, instead of initialization from file, so you close stream explicitly:

我没有在你的代码中看到问题,一切看起来都不错。要检查问题在于C1ZipFile我建议你从流初始化zip,而不是从文件初始化,所以你明确地关闭流:

//Open the zip file
using (Stream ZipStream = oFile.OpenRead())
using (C1ZipFile oZipFile = new C1ZipFile(ZipStream, false))
{
    // ...

Several other suggestions:

其他一些建议:

  • You do not need to call Close() method, with using (...), remove them.
  • 您不需要调用Close()方法,使用(...)删除它们。

  • Move xml processing (Loop hit elements) outsize zip processing, i.e. after zip file closeing, so you keep file opened as least as possible.
  • 移动xml处理(循环命中元素)超大zip处理,即在zip文件关闭后,所以你尽可能地保持文件打开。

#3


I'm just guessing: are you sure that oZipFile.Close() is enough? Perhaps you have to call oZipFile.Dispose() or oZipFile.Finalize() to be sure it has actually released the resources.

我只是猜测:你确定oZipFile.Close()就够了吗?也许你必须调用oZipFile.Dispose()或oZipFile.Finalize()来确保它实际上已经释放了资源。

#4


More then Likely it's not being disposed, anytime you access something outside of managed code(streams, files, etc.) you MUST dispose of them. I learned the hard way with Asp.NET and Image files, it will fill up your memory, crash your server, etc.

更多可能它没有被处理,无论何时你访问托管代码之外的东西(流,文件等),你必须处理它们。我用Asp.NET和Image文件学到了很多东西,它会填满你的内存,崩溃你的服务器等等。

#5


In the interest of completeness I am posing my working code as the changes came from more than one source.

为了完整起见,我正在构建我的工作代码,因为更改来自多个来源。

private void UnPackLegacyStats()
{
  DirectoryInfo oDirectory;
  XmlDocument oStatsXml;

  //Get the directory
  oDirectory = new DirectoryInfo(msLegacyStatZipsPath);

  //Check if the directory exists
  if (oDirectory.Exists)
  {
    //Loop files
    foreach (FileInfo oFile in oDirectory.GetFiles())
    {
      //Set empty xml
      oStatsXml = null;

      //Load file into a stream
      using (Stream oFileStream = oFile.OpenRead())
      {
        //Check if file is a zip file
        if (C1ZipFile.IsZipFile(oFileStream))
        {
          //Open the zip file
          using (C1ZipFile oZipFile = new C1ZipFile(oFileStream, false))
          {
            //Check if the zip contains the stats
            if (oZipFile.Entries.Contains("Stats.xml"))
            {
              //Get the stats as a stream
              using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
              {
                //Load the stats as xml
                oStatsXml = new XmlDocument();
                oStatsXml.Load(oStatsStream);
              }
            }
          }
        }
      }

      //Check if we have stats
      if (oStatsXml != null)
      {
        //Process XML here
      }

      //Delete the file
      oFile.Delete();
    }
  }
}

The main lesson I learned from this is to manage file access in one place in the calling code rather than letting other components manage their own file access. This is most apropriate when you want to use the file again after the other component has finished it's task.

我从中学到的主要教训是在调用代码中的一个位置管理文件访问,而不是让其他组件管理自己的文件访问。当您希望在其他组件完成任务后再次使用该文件时,这是最合适的。

Although this takes a little more code you can clearly see where the stream is disposed (at the end of the using), compared to having to trust that a component has correctly disposed of the stream.

虽然这需要更多代码,但您可以清楚地看到流处理的位置(在使用结束时),而不必相信组件已正确处理流。