خطأ في الوصول إلى الملف مع فيليسيستيمواتشير عند متعددة يتم إضافة ملفات إلى دليل

StackOverflow https://stackoverflow.com/questions/699538

سؤال

أنا الوقوع في مشكلة مع فيليسيستيمواتشير عند العديد من الملفات توضع في شاهد الدليل.أريد تحليل الملف في أقرب وقت كما هو وضعها في الدليل.عادة, الملف الأول يوزع ما يرام ، ولكن إضافة ثانية الملف إلى الدليل أسباب وصول القضية.أحيانا, الملف الأول حتى لا إعراب.هناك واحد فقط تطبيق تشغيل و مشاهدة هذا الدليل.في نهاية المطاف هذه العملية سوف تكون قيد التشغيل على أجهزة متعددة وأنها سوف يراقب دليل مشترك ولكن ملقم واحد فقط يمكن تحليل كل ملف كما يتم استيراد البيانات إلى قاعدة البيانات وليس هناك أي المفاتيح الأساسية.

هنا هو فيليسيستيمواتشير كود:

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

عند نقل الملف الثاني هو اصطياد

النظام.IO.IOException:يتعذر على العملية الوصول إلى الملف 'C: emp estFile.txt' لأنه قيد الاستخدام بواسطة عملية أخرى.

أتوقع أن نرى هذا الخطأ إذا كان يعمل على آلات متعددة ، ولكنه يعمل فقط على ملقم واحد في الوقت الراهن.لا ينبغي أن يكون هناك عملية أخرى باستخدام هذا الملف - علي خلق و نسخها إلى الدليل عندما يتم تشغيل التطبيق.

هل هذه الطريقة الصحيحة لإعداد فيليسيستيمواتشير?كيف يمكنني معرفة ما قفل هذا الملف ؟ لماذا لا تحليل كل الملفات هل يجب علي أن أغلق FileStream?أريد أن تبقي FileShare.لا شيء الخيار لأنني أريد فقط ملقم واحد إلى تحليل الملف الخادم الذي يحصل على الملف الأول يوزع عليه.

هل كانت مفيدة؟

المحلول

وهناك مشكلة نموذجية لهذا النهج هو الذي لا يزال يجري نسخ الملف في حين يتم تشغيل الحدث. ومن الواضح أن تحصل على استثناء لأنه يتم تأمين الملف أثناء النسخ. ومن المرجح خصوصا في الملفات الكبيرة استثناء.

وكحل مؤقت يمكن أولا نسخ الملف ثم تسميته والاستماع إلى الحدث إعادة تسمية.

وأو يكون الخيار الآخر أن يكون حلقة في حين التحقق ما إذا كان يمكن فتح الملف مع وصول الكتابة. إذا كان يمكن ستعرف تم الانتهاء من أن النسخ. C # رمز يمكن أن تبدو مثل هذا (في نظام الإنتاج قد ترغب في الحصول على أكبر عدد ممكن من المحاولة أو مهلة بدلا من while(true)):

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

ولكن من شأنه أن نهج آخر يتمثل في وضع ملف الزناد صغير في المجلد بعد اكتمال النسخ. سوف FileSystemWatcher بك الاستماع إلى ملف الزناد فقط.

نصائح أخرى

كنت قد تركت التعليق أعلاه, ولكن أنا لم يكن لديك ما يكفي من النقاط حتى الآن.

أعلى تصنيف الإجابة على هذا السؤال كتلة من التعليمات البرمجية التي تبدو مثل هذا:

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.

عند فتح الملف في طريقة OnChanged الخاص بك، وأنت تحديد FileShare.None، والتي وفقا ل<لأ href = "http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx "يختلط =" نوفولو noreferrer "> وثائق ، سوف يسبب أي محاولات أخرى لفتح ملف للفشل بينما كنت قد حصلت عليه مفتوحا. منذ كل ما عليك (ومراقب الخاص بك) تقوم به هو القراءة، حاول استخدام FileShare.Read بدلا من ذلك.

والحل بسيط يتمثل في التخلص من filesystemwatcher بمجرد تلقي الإخطار. قبل نسخ الملفات، جعل الانتظار الترابط الحالي حتى أنه يستقبل الحدث التخلص منها filesystemwatcher. ثم يمكنك الاستمرار نسخ الملف تغييرها دون مشاكل الوصول. كان لي نفس الشرط وفعلت ذلك بالضبط مثل ما ذكرتها. أنه يعمل.

ورمز مثال:

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

فيليسيستيمواتشير الحرائق مراقب.خلق الحدث مرتين كل واحد من إنشاء ملف 1ce عند نسخ ملف بدء تشغيل 2nd الوقت عند نسخ الملف انتهى.كل ما عليك القيام به هو تجاهل 1 الحدث ومعالجة الحدث للمرة الثانية.

مثال بسيط من معالج الحدث:

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

وأشعر مثال جيد على ما تريد هو ConfigureAndWatchHandler في log4net. وهم يستخدمون جهاز توقيت لإطلاق الحدث معالج الملف. أشعر بأن هذا ينتهي به الأمر إلى تنفيذ أنظف من الحلقة أثناء وجوده في آخر 0xA3 ل. لأولئك منكم الذين لا يريدون لاستخدام dotPeek لفحص ملف سأحاول أن أعطيك التعليمات البرمجية المتكررة هنا استنادا إلى رمز OP:

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

وكان لي مشكلة مماثلة. لمجرد بسبب FileSystemWatcher. أنا فقط تستخدم
Thread.Sleep ()؛

والتي تعمل بشكل جيد الآن. عندما يأتي الملف في الدليل أنه يدعو onCreated مرتين. ذلك مرة واحدة عندما ملف يتم copied.and مرة الثانية عند نسخ الانتهاء. لذلك كنت Thread.Sleep ()؛ لذلك سوف ننتظر قبل أدعو طلب & ReadFile ()؛

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

وكان لي نفس المشكلة في غضون DFS. وقد achived بلدي القرار بإضافة خطين فارغة إلى كل ملف. ثم ينتظر قانون بلدي لاثنين من خطوط فارغة في الملف. ثم لدي اليقين لقراءة البيانات كاملة من الملف.

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);
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top