Есть ли способ проверить, используется ли файл? - PullRequest
778 голосов
/ 18 мая 2009

Я пишу программу на C #, которой необходимо повторно обращаться к 1 файлу изображения. В большинстве случаев это работает, но если мой компьютер работает быстро, он попытается получить доступ к файлу до его сохранения в файловой системе и выдать ошибку: «Файл используется другим процессом» .

Я хотел бы найти способ обойти это, но все мои поиски в Google привели только к созданию проверок с использованием обработки исключений. Это против моей религии, поэтому мне было интересно, есть ли у кого-нибудь лучший способ сделать это?

Ответы [ 17 ]

2 голосов
/ 07 сентября 2017

Вы можете использовать мою библиотеку для доступа к файлам из нескольких приложений.

Вы можете установить его из nuget: Install-Package Xabe.FileLock

Если вы хотите больше информации об этом, проверьте https://github.com/tomaszzmuda/Xabe.FileLock

ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
    using(fileLock)
    {
        // file operations here
    }
}

Метод fileLock.Acquire вернет true, только если может заблокировать файл исключительно для этого объекта. Но приложение, которое загружает файл, должно делать это и при блокировке файла. Если объект недоступен, метод возвращает false.

2 голосов
/ 22 августа 2016

Вот код, который, насколько я могу судить, делает то же самое, что и принятый ответ, но с меньшим кодом:

    public static bool IsFileLocked(string file)
    {
        try
        {
            using (var stream = File.OpenRead(file))
                return false;
        }
        catch (IOException)
        {
            return true;
        }        
    }

Однако я думаю, что более надежно сделать это следующим образом:

    public static void TryToDoWithFileStream(string file, Action<FileStream> action, 
        int count, int msecTimeOut)
    {
        FileStream stream = null;
        for (var i = 0; i < count; ++i)
        {
            try
            {
                stream = File.OpenRead(file);
                break;
            }
            catch (IOException)
            {
                Thread.Sleep(msecTimeOut);
            }
        }
        action(stream);
    }
1 голос
/ 02 сентября 2014

По моему опыту, вы обычно хотите сделать это, затем «защитить» свои файлы, чтобы сделать что-то причудливое, а затем использовать «защищенные» файлы. Если у вас есть только один файл, который вы хотите использовать подобным образом, вы можете использовать трюк, который объясняется в ответе Джереми Томпсона. Однако, если вы попытаетесь сделать это для большого количества файлов (скажем, например, когда пишете установщик), вам будет очень больно.

Очень элегантный способ решить эту проблему - использовать тот факт, что ваша файловая система не позволит вам изменить имя папки, если один из файлов там используется. Сохраните папку в той же файловой системе, и она будет работать как шарм.

Обратите внимание, что вы должны знать об очевидных способах использования этой возможности. В конце концов, файлы не будут заблокированы. Также помните, что есть и другие причины, которые могут привести к сбою операции Move. Очевидно, что правильная обработка ошибок (MSDN) может помочь здесь.

var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here
var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N"));

try
{
    Directory.Move(originalFolder, someFolder);

    // Use files
}
catch // TODO: proper exception handling
{
    // Inform user, take action
}
finally
{
    Directory.Move(someFolder, originalFolder);
}

Для отдельных файлов я бы придерживался предложения о блокировке, представленного Джереми Томпсоном.

0 голосов
/ 09 апреля 2019

Хотелось бы что-нибудь подобное?

var fileWasWrittenSuccessfully = false;
while (fileWasWrittenSuccessfully == false)
{
    try
    {
        lock (new Object())
        {
            using (StreamWriter streamWriter = new StreamWriter(filepath.txt"), true))
            {
                streamWriter.WriteLine("text");
            }
        }

        fileWasWrittenSuccessfully = true;
    }
    catch (Exception)
    {

    }
}
0 голосов
/ 05 апреля 2018

Мне интересно посмотреть, вызывает ли это какие-либо рефлексы WTF. У меня есть процесс, который создает и впоследствии запускает документ PDF из консольного приложения. Однако я имел дело со слабостью, когда, если бы пользователь запускал процесс несколько раз, генерируя один и тот же файл, не закрывая предварительно сгенерированный файл, приложение выдает исключение и умирает. Это было довольно частым явлением, потому что имена файлов основаны на номерах коммерческих предложений.

Вместо того, чтобы терпеть неудачу в такой изящной манере, я решил положиться на автоматическое увеличение версий файлов:

private static string WriteFileToDisk(byte[] data, string fileName, int version = 0)
{
    try
    {
        var versionExtension = version > 0 ? $"_{version:000}" : string.Empty;
        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf");
        using (var writer = new FileStream(filePath, FileMode.Create))
        {
            writer.Write(data, 0, data.Length);
        }
        return filePath;
    }
    catch (IOException)
    {
        return WriteFileToDisk(data, fileName, ++version);
    }
}

Возможно, для блока catch может быть уделено больше внимания, чтобы убедиться, что я ловлю правильные исключения IOException. Я, вероятно, также очистлю хранилище приложения при запуске, так как эти файлы в любом случае должны быть временными.

Я понимаю, что это выходит за рамки вопроса ОП просто проверять, используется ли файл, но это действительно была проблема, которую я искал, когда приехал сюда, так что, возможно, это будет полезно кому-то еще.

0 голосов
/ 18 мая 2009

Попробуйте переместить / скопировать файл во временную папку. Если вы можете, у него нет блокировки, и вы можете безопасно работать в временном каталоге, не получая блокировки. В противном случае попробуйте переместить его снова через x секунд.

0 голосов
/ 17 января 2012

Я использую этот обходной путь, но у меня есть промежуток времени между проверкой блокировки файла с помощью функции IsFileLocked и открытием файла. В этот промежуток времени файл может открыть какой-то другой поток, поэтому я получу IOException.

Итак, я добавил дополнительный код для этого. В моем случае я хочу загрузить XDocument:

        XDocument xDoc = null;

        while (xDoc == null)
        {
            while (IsFileBeingUsed(_interactionXMLPath))
            {
                Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait...");
                Thread.Sleep(100);
            }
            try
            {
                xDoc = XDocument.Load(_interactionXMLPath);
            }
            catch
            {
                Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!");
            }
        }

Что ты думаешь? Могу ли я что-то изменить? Может быть, мне вообще не приходилось использовать функцию IsFileBeingUsed?

Спасибо

...