2009-03-31 8 views
36

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

public void Run() { 
    FileSystemWatcher watcher = new FileSystemWatcher("C:\\temp"); 
    watcher.NotifyFilter = NotifyFilters.FileName; 
    watcher.Filter = "*.txt"; 

    watcher.Created += new FileSystemEventHandler(OnChanged); 

    watcher.EnableRaisingEvents = true; 
    System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite); 
} 

तो विधि उस फ़ाइल को पार्स करता है:

private void OnChanged(object source, FileSystemEventArgs e) { 
    string line = null; 

    try { 
    using (FileStream fs = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.None)) { 
     using (StreamReader sr = new StreamReader(fs)) { 
     while (sr.EndOfStream == false) { 
      line = sr.ReadLine(); 
      //parse the line and insert into the database 
     } 
     } 
    } 
    } 
    catch (IOException ioe) { 
    Console.WriteLine("OnChanged: Caught Exception reading file [{0}]", ioe.ToString()); 
    } 

जब दूसरी फ़ाइल चलती है, यह बढ़ रहा है

System.IO

यहाँ FileSystemWatcher कोड है I.OException: प्रक्रिया फ़ाइल 'C: \ Temp \ TestFile.txt' तक नहीं पहुंच सकती है क्योंकि इसका उपयोग किसी अन्य प्रक्रिया द्वारा किया जा रहा है।

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

क्या यह फ़ाइल सिस्टम सिस्टम स्थापित करने का उचित तरीका है? मैं कैसे देख सकता हूं कि इस फाइल पर लॉक क्या है? यह दोनों फ़ाइलों को पार्स क्यों नहीं करता है - क्या मुझे फ़ाइलस्ट्रीम बंद करना है? मैं FileShare.None विकल्प रखना चाहता हूं क्योंकि मैं केवल एक सर्वर को फ़ाइल को पार्स करना चाहता हूं - जो फ़ाइल फ़ाइल में आता है वह पहले इसे पार करता है।

उत्तर

50

इस दृष्टिकोण की एक सामान्य समस्या यह है कि ईवेंट ट्रिगर होने पर फ़ाइल की प्रतिलिपि बनाई जा रही है। जाहिर है, आपको अपवाद मिलेगा क्योंकि प्रतिलिपि के दौरान फ़ाइल लॉक है। एक अपवाद विशेष रूप से बड़ी फाइलों पर होने की संभावना है।

एक कामकाज के रूप में आप पहले फ़ाइल की प्रतिलिपि बना सकते हैं और फिर उसका नाम बदल सकते हैं और नामकरण कार्यक्रम सुन सकते हैं।

या किसी अन्य विकल्प में थोड़ी देर लूप जांच होगी कि फ़ाइल को लेखन पहुंच के साथ खोला जा सकता है या नहीं। यदि यह आपको पता चलेगा कि प्रतिलिपि पूरी हो चुकी है।

/// <summary> 
/// Waits until a file can be opened with write permission 
/// </summary> 
public static void WaitReady(string fileName) 
{ 
    while (true) 
    { 
     try 
     { 
      using (Stream stream = System.IO.File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) 
      { 
       if (stream != null) 
       { 
        System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName)); 
        break; 
       } 
      } 
     } 
     catch (FileNotFoundException ex) 
     { 
      System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message)); 
     } 
     catch (IOException ex) 
     { 
      System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message)); 
     } 
     catch (UnauthorizedAccessException ex) 
     { 
      System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message)); 
     } 
     Thread.Sleep(500); 
    } 
} 

फिर भी एक और दृष्टिकोण फ़ोल्डर में एक छोटे से ट्रिगर फ़ाइल रखना होगा: सी # कोड ऐसा दिखाई दे सकता है (एक उत्पादन प्रणाली में आप पुनः की अधिकतम संख्या या एक while(true) के बजाय समय समाप्त करने के लिए चाहते हो सकता है) प्रतिलिपि पूरा होने के बाद। आपका FileSystemWatcher केवल ट्रिगर फ़ाइल को सुनेंगे।

+0

ये परीक्षण फ़ाइलें छोटी हैं - छोटी पाठ की केवल कुछ पंक्तियां हैं, इसलिए इसे प्रतिलिपि के दौरान बहुत लंबे समय तक लॉक नहीं किया जाना चाहिए। मैं यह जांचने के लिए लूप कैसे करूं कि फ़ाइल लिखने के लिए तैयार है या नहीं? –

+0

धन्यवाद! मुझे शर्म आती है कि मैं इसके समाधान के रूप में थोड़ी देर के लूप के बारे में नहीं सोच सकता था - मुझे किसी अन्य ऐप की आउटपुट फ़ाइल का अद्यतन संस्करण पढ़ने की जरूरत थी। मुझे प्रति लिखने के लिए कई कार्यक्रम मिलते हैं - इसलिए मैंने एक टाइमर का उपयोग किया जो हर बार रीसेट होता है और अद्यतन फ़ाइल को पढ़ने वाले कोड को ट्रिगर करने से पहले एक्स सेकेंड के लिए प्रतीक्षा करता है। लेकिन अगर ऐप एक्स सेकेंड के भीतर दो बार लिखा गया तो मुझे अपडेट याद आया। – Gishu

+3

+1 लेकिन आपको वास्तव में प्रतीक्षा करने के लिए अधिकतम समय होना चाहिए ताकि प्रक्रिया हमेशा के लिए लॉक न हो –

0

मुझे डीएफएस के भीतर एक ही समस्या थी। मेरा संकल्प प्रत्येक फ़ाइल में दो खाली लाइन जोड़कर प्राप्त किया गया था। फिर मेरा कोड फ़ाइल में दो खाली लाइनों के लिए इंतजार कर रहा है। तब मुझे फ़ाइल से पूरा डेटा पढ़ने के लिए निश्चितता है।

3

जब आप अपने OnChanged विधि में फ़ाइल खोलने, आप FileShare.None निर्दिष्ट कर रहे हैं, जो the documentation के अनुसार, किसी भी अन्य प्रयास विफल करने के लिए फ़ाइल को खोलने का कारण होगा आप इसे खोल रखी है, जबकि। चूंकि आप (और आपका दर्शक) पढ़ रहे हैं, इसके बजाय FileShare.Read का उपयोग करने का प्रयास करें।

+0

मूर्खतापूर्ण हैक्स के बिना सही समाधान। –

2

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

उदाहरण कोड:

public void TestWatcher() 
{ 
    using (var fileWatcher = new FileSystemWatcher()) 
    { 

     string path = @"C:\sv"; 
     string file = "pos.csv"; 

     fileWatcher.Path = path; 
     fileWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite; 
     fileWatcher.Filter = file; 

     System.EventHandler onDisposed = (sender,args) => 
     { 
      eve.Set(); 
     }; 

     FileSystemEventHandler onFile = (sender, fileChange) => 
     { 
      fileWatcher.EnableRaisingEvents = false; 
      Thread t = new Thread(new ParameterizedThreadStart(CopyFile)); 
      t.Start(fileChange.FullPath); 
      if (fileWatcher != null) 
      { 
       fileWatcher.Dispose(); 
      } 
      proceed = false; 
     }; 

     fileWatcher.Changed += onFile; 
     fileWatcher.Created += onFile; 
     fileWatcher.Disposed+= onDisposed; 
     fileWatcher.EnableRaisingEvents = true; 

     while (proceed) 
     { 
      if (!proceed) 
      { 
       break; 
      } 
     } 
    } 
} 

public void CopyFile(object sourcePath) 
{ 
    eve.WaitOne(); 
    var destinationFilePath = @"C:\sv\Co"; 
    if (!string.IsNullOrEmpty(destinationFilePath)) 
    { 
     if (!Directory.Exists(destinationFilePath)) 
     { 
      Directory.CreateDirectory(destinationFilePath); 
     } 
     destinationFilePath = Path.Combine(destinationFilePath, "pos.csv"); 
    }   

    File.Copy((string)sourcePath, destinationFilePath); 
} 
1

मैं तुम क्या चाहते log4net में ConfigureAndWatchHandler है का एक अच्छा उदाहरण है। वे फ़ाइल हैंडलर ईवेंट को आग लगाने के लिए टाइमर का उपयोग करते हैं। मुझे लगता है कि यह 0xA3 की पोस्ट में लूप के क्लीनर कार्यान्वयन के रूप में समाप्त होता है। आप में से जो है कि नहीं चाहता फ़ाइल का परीक्षण करने के लिए dotPeek का उपयोग करने के लिए मैं यहाँ आपको एक कोड स्निपेट देने के लिए ओपी कोड के आधार पर की कोशिश करेंगे:

private System.Threading.Timer _timer;  

public void Run() { 
    //setup filewatcher 
    _timer = new System.Threading.Timer(new TimerCallback(OnFileChange), (object) null, -1, -1); 
} 

private void OnFileChange(object state) 
{ 
    try 
    { 
    //handle files 
    } 
    catch (Exception ex) 
    { 
     //log exception 
     _timer.Change(500, -1); 
    } 
} 
9

मैं एक टिप्पणी के ऊपर छोड़ दिया है, लेकिन मेरे पास अभी तक पर्याप्त अंक नहीं हैं।

इस सवाल का जवाब यह है कि टॉप रेटेड इस तरह दिखना कोड का एक खंड है:

using (Stream stream = System.IO.File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) 
{ 
    if (stream != null) 
    { 
     System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName)); 
     break; 
    } 
} 

का उपयोग कर FileShare.ReadWrite सेटिंग है कि यह करने के लिए फ़ाइल मूल रूप से कह पहुंच का अनुरोध कर रहा है के साथ समस्या "मैं चाहता हूँ इस फ़ाइल को पढ़ें/लिखें, लेकिन अन्य इसे भी पढ़ सकते हैं/लिख सकते हैं। " यह दृष्टिकोण हमारी स्थिति में विफल रहा। रिमोट ट्रांसफर प्राप्त करने वाली प्रक्रिया ने फाइल पर लॉक नहीं लगाया, लेकिन यह सक्रिय रूप से इसे लिख रहा था। हमारा डाउनस्ट्रीम कोड (SharpZipLib) "उपयोग में फ़ाइल" अपवाद के साथ विफल रहा था क्योंकि यह फ़ाइल को FileShare.Read ("मैं पढ़ने के लिए फ़ाइल चाहता हूं, और केवल अन्य प्रक्रियाओं को भी पढ़ने देता हूं") के साथ फ़ाइल खोलने की कोशिश कर रहा था। चूंकि फ़ाइल खोलने वाली प्रक्रिया पहले से ही लिख रही थी, इसलिए यह अनुरोध विफल हो गया।

हालांकि, उपर्युक्त प्रतिक्रिया में कोड बहुत ही आराम से है। FileShare.ReadWrite का उपयोग करके, यह फ़ाइल तक पहुंच प्राप्त करने में सफल रहा था (क्योंकि यह एक साझा प्रतिबंध के लिए पूछ रहा था जिसे सम्मानित किया जा सकता था), लेकिन डाउनस्ट्रीम कॉल विफल रहा।

File.Open करने के लिए कॉल में हिस्सा सेटिंग या तो FileShare.Read या FileShare.None, और नहींFileShare.ReadWrite होना चाहिए।

2

FileSystemWatcher आग हर एक फ़ाइल निर्माण 1ce जब फ़ाइल प्रतिलिपि प्रारंभ किया जाता है और 2 बार जब फ़ाइल प्रतिलिपि समाप्त हो गया है के लिए घटना दो बार watcher.Created। आपको बस दूसरी घटना को अनदेखा करना है और दूसरी बार प्रक्रिया की प्रक्रिया को नजरअंदाज करना है।

ईवेंट हैंडलर का एक सरल उदाहरण:

private bool _fileCreated = false; 
private void FileSystemWatcher_FileCreated(object sender, FileSystemEventArgs e) 
{ 
    if (_fileCreated) 
    { 
     ReadFromFile();//just an example method call to access the new file 
    } 

    _fileCreated = !_fileCreated; 
} 
0
public static BitmapSource LoadImageNoLock(string path) 
{ 
    while (true) 
    { 
     try 
     { 
      var memStream = new MemoryStream(File.ReadAllBytes(path)); 
      var img = new BitmapImage(); 
      img.BeginInit(); 
      img.StreamSource = memStream; 
      img.EndInit(); 
      return img; 
      break; 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 
    } 
} 
1

मैं इसी तरह की समस्या थी। यह सिर्फ फाइलसिस्टम वाटर के कारण है। मैंने अभी
थ्रेड का उपयोग किया। नींद();

और अब यह ठीक काम कर रहा है। जब फ़ाइल निर्देशिका में आती है तो यह दो बार तैयार की जाती है। तो एक बार जब फ़ाइल की प्रतिलिपि बनाई जा रही हो। और दूसरी बार जब कॉपी पूर्ण हो जाए। इसके लिए मैंने थ्रेड का इस्तेमाल किया।नींद(); तो मैं रीडफाइल() को कॉल करने से पहले इंतजार करूंगा;

private static void OnCreated(object source, FileSystemEventArgs e) 
    { 
     try 
     { 
      Thread.Sleep(5000); 
      var data = new FileData(); 
      data.ReadFile(e.FullPath);     
     } 
     catch (Exception ex) 
     { 
      WriteLogforError(ex.Message, String.Empty, filepath); 
     } 
    } 
संबंधित मुद्दे