c# - क्या फ़ाइल का उपयोग करने के लिए कोई तरीका है या नहीं?




.net file (12)

आप इस पर थ्रेड रेस कंडीशन से पीड़ित हो सकते हैं, जिसमें सुरक्षा भेद्यता के रूप में उपयोग किए जाने वाले दस्तावेज उदाहरण हैं। यदि आप जांचते हैं कि फ़ाइल उपलब्ध है, लेकिन फिर कोशिश करें और इसका उपयोग करें, तो आप उस बिंदु पर फेंक सकते हैं, जो एक दुर्भावनापूर्ण उपयोगकर्ता आपके कोड में बल देने और उसका शोषण करने के लिए उपयोग कर सकता है।

आपकी सबसे अच्छी शर्त एक कोशिश पकड़ / आखिरकार है जो फाइल हैंडल प्राप्त करने का प्रयास करती है।

try
{
   using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
   {
        // File/Stream manipulating code here
   }
} catch {
  //check here why it failed and ask user to retry if the file is in use.
}

मैं सी # में एक प्रोग्राम लिख रहा हूं जिसे बार-बार 1 छवि फ़ाइल तक पहुंचने की आवश्यकता है। अधिकांश समय यह काम करता है, लेकिन यदि मेरा कंप्यूटर तेजी से चल रहा है, तो यह फाइल सिस्टम पर वापस सहेजने से पहले फ़ाइल तक पहुंचने का प्रयास करेगा और एक त्रुटि फेंक देगा: "किसी अन्य प्रक्रिया द्वारा उपयोग में फ़ाइल करें"

मैं इसके चारों ओर एक रास्ता खोजना चाहता हूं, लेकिन मेरे सभी गुगलिंग ने केवल अपवाद हैंडलिंग का उपयोग कर चेक बनाने के लिए उत्पन्न किया है। यह मेरे धर्म के खिलाफ है, इसलिए मैं सोच रहा था कि क्या किसी के पास ऐसा करने का बेहतर तरीका है?


आप एक कार्य वापस कर सकते हैं जो आपको उपलब्ध होने पर स्ट्रीम प्रदान करता है। यह एक सरल समाधान है, लेकिन यह एक अच्छा प्रारंभिक बिंदु है। यह धागा सुरक्षित है।

private async Task<Stream> GetStreamAsync()
{
    try
    {
        return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write);
    }
    catch (IOException)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return await GetStreamAsync();
    }
}

आप इस धारा का सामान्य रूप से उपयोग कर सकते हैं:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}

इरादे के रूप में बस अपवाद का उपयोग करें। स्वीकार करें कि फ़ाइल उपयोग में है और फिर से प्रयास करें, जब तक कि आपकी कार्य पूरी नहीं हो जाती। यह भी सबसे कुशल है क्योंकि आप अभिनय से पहले राज्य की जांच करने वाले किसी भी चक्र को बर्बाद नहीं करते हैं।

उदाहरण के लिए नीचे दिए गए फ़ंक्शन का उपयोग करें

TimeoutFileAction(() => { System.IO.File.etc...; return null; } );

पुन: प्रयोज्य विधि जो 2 सेकंड के बाद समाप्त होती है

private T TimeoutFileAction<T>(Func<T> func)
{
    var started = DateTime.UtcNow;
    while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
    {
        try
        {
            return func();                    
        }
        catch (System.IO.IOException exception)
        {
            //ignore, or log somewhere if you want to
        }
    }
    return default(T);
}

ऊपर दिए गए स्वीकृत उत्तर एक समस्या का सामना करते हैं, जहां फ़ाइल फ़ाइल के साथ लिखने के लिए फ़ाइल खोला गया है। रीड मोड या यदि फ़ाइल में केवल-पढ़ने योग्य विशेषता है तो कोड काम नहीं करेगा। यह संशोधित समाधान सबसे विश्वसनीय रूप से काम करता है, दो चीजों को ध्यान में रखना (स्वीकार्य समाधान के लिए भी सच है):

  1. यह उन फ़ाइलों के लिए काम नहीं करेगा जो एक लेखन शेयर मोड के साथ खोला गया है
  2. यह खाता थ्रेडिंग मुद्दों को ध्यान में रखता नहीं है, इसलिए आपको इसे लॉक करने या थ्रेडिंग समस्याओं को अलग से संभालने की आवश्यकता होगी।

उपरोक्त को ध्यान में रखते हुए, यह जांचता है कि फ़ाइल को या तो पढ़ने के लिए लॉक किया गया है या पढ़ने को रोकने के लिए बंद कर दिया गया है :

public static bool FileLocked(string FileName)
{
    FileStream fs = null;

    try
    {
        // NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked
        fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it's locked by another process for writing
    }
    catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx
    {
        // This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode
        try
        {
            fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None);
        }
        catch (Exception)
        {
            return true; // This file has been locked, we can't even open it to read
        }
    }
    catch (Exception)
    {
        return true; // This file has been locked
    }
    finally
    {
        if (fs != null)
            fs.Close();
    }
    return false;
}

मुझे एक ही समस्या थी और कुछ ऐसा काम करता था जो काम करने लग रहा था, हालांकि यह अपवाद हैंडलिंग का उपयोग कर रहा था ...

मैंने अनंत लूप को रोकने के लिए 100 बार कोशिश करने के लिए एक काउंटर लगाया।

निचे देखो...

    private void uploadFiles(string filename)
    {
        try
        {
            string fromFileAndPath = Properties.Settings.Default.Path + "\\" + filename;
            string toFileAndPath = Properties.Settings.Default.CopyLocation + "\\" + filename;
            if (!File.Exists(toFileAndPath))
            {
                FileInfo imgInfo = new FileInfo(fromFileAndPath);
                bool copied = false;
                int counter = 0;
                while (!copied && counter < 100) //While was added as I was getting "The process cannot access the file because it is being used by another process" errors.
                {
                    try
                    {
                        counter++;
                        imgInfo.CopyTo(toFileAndPath);
                        copied = true;
                    }
                    catch
                    {
                        //If it cannot copy catch
                    }
                }
                if (counter > 100)
                    throw new Exception("Unable to copy file!");
                Thread.Sleep(1);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("An error occurred: " + ex.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

मुझे पता है कि Win32 अनन्य लॉक एपीआई का उपयोग करने का एकमात्र तरीका है जो बहुत तेज़ नहीं है, लेकिन उदाहरण मौजूद हैं।

ज्यादातर लोग, इसके लिए एक आसान समाधान के लिए, बस कोशिश / पकड़ / नींद लूप करने के लिए।


मेरे अनुभव में, आप आमतौर पर ऐसा करना चाहते हैं, फिर अपनी फ़ाइलों को कुछ फैंसी करने के लिए 'सुरक्षित' करें और फिर 'संरक्षित' फ़ाइलों का उपयोग करें। यदि आपके पास सिर्फ एक फ़ाइल है जिसे आप इस तरह उपयोग करना चाहते हैं, तो आप जेरेमी थॉम्पसन द्वारा उत्तर में बताई गई चाल का उपयोग कर सकते हैं। हालांकि, अगर आप इसे बहुत सारी फाइलों पर करने का प्रयास करते हैं (कहें, उदाहरण के लिए जब आप एक इंस्टॉलर लिख रहे हों), तो आप काफी हद तक चोट पहुंच रहे हैं।

इसका हल करने का एक बहुत ही सुरुचिपूर्ण तरीका इस तथ्य का उपयोग करके है कि आपकी फाइल सिस्टम आपको फ़ोल्डर नाम बदलने की इजाजत नहीं देगी यदि फाइलों में से एक का उपयोग किया जा रहा है। फ़ोल्डर को एक ही फाइल सिस्टम में रखें और यह एक आकर्षण की तरह काम करेगा।

ध्यान दें कि आपको उन स्पष्ट तरीकों से अवगत होना चाहिए जिनके लिए इसका शोषण किया जा सकता है। आखिरकार, फाइलों को बंद नहीं किया जाएगा। साथ ही, ध्यान रखें कि अन्य कारण भी हैं जिनके परिणामस्वरूप आपके Move ऑपरेशन विफल हो सकते हैं। जाहिर है उचित त्रुटि हैंडलिंग (एमएसडीएन) यहां मदद कर सकती है।

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);
}

व्यक्तिगत फाइलों के लिए मैं जेरेमी थॉम्पसन द्वारा पोस्ट किए गए लॉकिंग सुझाव के साथ रहूंगा।


मैं इस वर्कअराउंड का उपयोग करता हूं, लेकिन मेरे पास टाइमफैन है जब मैं 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 फ़ंक्शन का उपयोग करने की ज़रूरत नहीं है?

धन्यवाद


यहां कुछ कोड दिया गया है जहां तक ​​मैं सबसे अच्छा कह सकता हूं स्वीकार्य उत्तर के समान ही है लेकिन कम कोड के साथ:

    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);
    }

शायद आप एक FileSystemWatcher उपयोग कर सकते हैं और बदले गए ईवेंट के लिए देख सकते हैं।

मैंने इसे स्वयं नहीं इस्तेमाल किया है, लेकिन यह एक शॉट के लायक हो सकता है। यदि फाइल सिस्टम सिस्टम इस मामले के लिए थोड़ा भारी हो जाता है, तो मैं कोशिश / पकड़ / नींद पाश के लिए जाऊंगा।


इस समाधान पर अपडेट किया गया नोट : FileAccess.ReadWrite साथ जांचना केवल-पढ़ने वाली फ़ाइलों के लिए विफल हो जाएगा, इसलिए FileAccess.Read साथ FileAccess.Read लिए समाधान संशोधित किया गया है। हालांकि यह समाधान काम करता है क्योंकि FileAccess.Read से जांचने का प्रयास कर रहा है अगर फ़ाइल में लिखना या पढ़ना लॉक है, हालांकि, यह समाधान काम नहीं करेगा अगर फ़ाइल में लिखने या पढ़ने के लिए लॉक नहीं है, यानी यह FileShare.Read या FileShare.Write एक्सेस के साथ खोला गया है (पढ़ने या लिखने के लिए)।

मूल: मैंने पिछले कई सालों से इस कोड का उपयोग किया है, और मुझे इसके साथ कोई समस्या नहीं है।

अपवादों का उपयोग करने के बारे में अपनी हिचकिचाहट को समझें, लेकिन आप उन्हें हर समय से नहीं बचा सकते हैं:

protected virtual bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

static bool FileInUse(string path)
    {
        try
        {
            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
            {
                fs.CanWrite
            }
            return false;
        }
        catch (IOException ex)
        {
            return true;
        }
    }

string filePath = "C:\\Documents And Settings\\yourfilename";
bool isFileInUse;

isFileInUse = FileInUse(filePath);

// Then you can do some checking
if (isFileInUse)
   Console.WriteLine("File is in use");
else
   Console.WriteLine("File is not in use");

उम्मीद है की यह मदद करेगा!





file-locking