Почему этот файл нельзя удалить после использования C1ZipFile?
Вопрос
Следующий код выдает исключение 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();
}
}
}
Я изо всех сил пытаюсь понять, где файл еще можно заблокировать.Все объекты, которые могут удерживать дескриптор файла, находятся в блоках использования и явно закрыты.
Это как-то связано с использованием объектов FileInfo, а не строк, возвращаемых статическим методом GetFiles?
Есть идеи?
Решение
Я предполагаю, что вы получаете ошибку при вызове oFile.Delete.Мне удалось воспроизвести эту ошибку.Интересно, что ошибка возникает только тогда, когда файл нет zip-файл.Это то поведение, которое вы наблюдаете?
Похоже, что вызов C1ZipFile.IsZipFile не освобождает файл, если он не является zip-файлом.Мне удалось избежать этой проблемы, используя FileStream вместо передачи пути к файлу в виде строки (функция IsZipFile принимает и то, и другое).
Итак, следующая модификация вашего кода работает:
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();
}
}
В ответ на исходный вопрос в теме:Я не знаю, можно ли узнать, можно ли удалить файл, не пытаясь его удалить.Вы всегда можете написать функцию, которая пытается удалить файл и обнаруживает ошибку, если это не удается, а затем возвращает логическое значение, указывающее, было ли удаление успешным.
Другие советы
Я не вижу проблем в вашем коде, все в порядке.Чтобы проверить, заключается ли проблема в C1ZipFile, я предлагаю вам инициализировать zip из потока вместо инициализации из файла, поэтому вы явно закрываете поток:
//Open the zip file
using (Stream ZipStream = oFile.OpenRead())
using (C1ZipFile oZipFile = new C1ZipFile(ZipStream, false))
{
// ...
Несколько других предложений:
- Вам не нужно вызывать метод Close(), т.к. с использованием (...), удалить их.
- Переместить обработку XML (элементы циклического попадания) за пределы обработки zip, т.е.после закрытия zip-файла, чтобы файл оставался открытым как можно реже.
Я просто предполагаю:вы уверены, что oZipFile.Close() достаточно?Возможно, вам придется вызвать oZipFile.Dispose() или oZipFile.Finalize(), чтобы убедиться, что ресурсы действительно освобождены.
Более того, скорее всего, он не удаляется, каждый раз, когда вы получаете доступ к чему-то за пределами управляемого кода (потоки, файлы и т. д.), вы ДОЛЖНЫ избавиться от них.Я на собственном горьком опыте научился работе с файлами Asp.NET и изображения: это заполнит вашу память, приведет к сбою вашего сервера и т. д.
Для полноты картины я представляю свой рабочий код, поскольку изменения произошли из более чем одного источника.
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();
}
}
}
Главный урок, который я извлек из этого, — управлять доступом к файлам в одном месте вызывающего кода, а не позволять другим компонентам управлять собственным доступом к файлам.Это наиболее удобно, если вы хотите снова использовать файл после того, как другой компонент завершил свою задачу.
Хотя для этого требуется немного больше кода, вы можете четко видеть, где расположен поток (в конце использования), по сравнению с необходимостью полагаться на то, что компонент правильно удалил поток.