Pertanyaan C # FileSystemWatcher, Cara mengetahui file disalin sepenuhnya ke folder jam tangan


Saya mengembangkan aplikasi .net, di mana saya menggunakan kelas FileSystemWatcher dan melampirkan acara yang Dibuatnya di folder. Saya harus melakukan tindakan pada acara ini (mis. Menyalin file ke beberapa lokasi lain). Ketika saya meletakkan ukuran besar ke dalam folder arloji yang terpasang, acara segera dinaikkan bahkan proses penyalinan file masih belum selesai. Saya tidak ingin memeriksanya dengan metode file.open.

Apakah ada cara untuk memberi tahu bahwa proses penyalinan file saya ke folder jam telah selesai dan kemudian acara saya terbakar.


5
2017-11-25 14:16


asal


Jawaban:


Sungguh menyedihkan bahwa FileSystemWatcher (dan API ReadDirectoryChangesW yang mendasari) tidak memberikan cara untuk mendapatkan pemberitahuan ketika file baru telah sepenuhnya dibuat.

Cara terbaik dan teraman untuk hal ini yang saya temui sejauh ini (dan itu tidak bergantung pada pengatur waktu) seperti ini:

Setelah menerima acara yang Dibuat, mulai utas yang, dalam satu lingkaran, periksa apakah file masih terkunci (menggunakan interval coba lagi yang tepat dan jumlah coba ulang maksimum). Satu-satunya cara untuk memeriksa apakah file terkunci adalah dengan mencoba membukanya dengan akses eksklusif: Jika berhasil (tidak melempar IOException), maka File selesai menyalin, dan utas Anda dapat meningkatkan kejadian yang sesuai (mis. FileCopyCompleted).


3
2018-03-30 22:53



Saya memiliki masalah yang sama persis, dan memecahkannya seperti ini:

  1. Set FileSystemWatcher untuk memberi tahu saat file dibuat dan ketika mereka dimodifikasi.
  2. Saat pemberitahuan muncul:

    Sebuah. Jika tidak ada pengaturan waktu untuk nama file ini (lihat di bawah), atur timer untuk kedaluwarsa dalam interval yang sesuai (saya biasanya menggunakan 1 detik).

    b. Jika ada pengatur waktu untuk nama file ini, batalkan pewaktu dan atur yang baru agar kedaluwarsa dalam interval yang sama.

Ketika timer kedaluwarsa, Anda tahu bahwa file yang terkait telah dibuat atau diubah dan telah disentuh untuk interval waktu. Ini berarti bahwa salinan / modifikasi mungkin dilakukan dan Anda sekarang dapat memprosesnya.


2
2017-11-25 14:21



Anda dapat mendengarkan acara yang dimodifikasi, dan memulai pengatur waktu. Jika acara yang dimodifikasi dibangkitkan lagi, atur ulang pengatur waktu. Ketika penghitung waktu telah mencapai nilai tertentu tanpa mengubah acara yang ditingkatkan, Anda dapat mencoba melakukan salin.


1
2017-11-25 14:20



Saya berlangganan ke Changed- dan Renamed-Menemukan dan mencoba untuk mengganti nama file pada setiap Changed-Menemukan penangkapan IOExceptions. Jika penggantian nama berhasil, salinan telah selesai dan Rename-acara dipecat hanya sekali.


1
2018-05-05 08:25



Tiga masalah dengan FileSystemWatcher, yang pertama adalah bahwa ia dapat mengirim peristiwa pembuatan duplikat sehingga Anda memeriksanya dengan sesuatu seperti:

this.watcher.Created += (s, e) =>
{
    if (!this.seen.ContainsKey(e.FullPath) 
        || (DateTime.Now - this.seen[e.FullPath]) > this.seenInterval)
    {
        this.seen[e.FullPath] = DateTime.Now;
        ThreadPool.QueueUserWorkItem(
            this.WaitForCreatingProcessToCloseFileThenDoStuff, e.FullPath);
    }
};

dimana this.seen adalah Dictionary<string, DateTime> dan this.seenInterval adalah TimeSpan.

Selanjutnya, Anda harus menunggu pembuat file selesai menulisnya (masalah yang muncul dalam pertanyaan). Dan, ketiga, Anda harus berhati-hati karena terkadang peristiwa pembuatan file dilemparkan sebelum file dapat dibuka tanpa memberi Anda FileNotFoundException tetapi juga dapat dihapus sebelum Anda dapat memegangnya yang juga memberikan FileNotFoundException.

private void WaitForCreatingProcessToCloseFileThenDoStuff(object threadContext)
{
    // Make sure the just-found file is done being
    // written by repeatedly attempting to open it
    // for exclusive access.
    var path = (string)threadContext;
    DateTime started = DateTime.Now;
    DateTime lastLengthChange = DateTime.Now;
    long lastLength = 0;
    var noGrowthLimit = new TimeSpan(0, 5, 0);
    var notFoundLimit = new TimeSpan(0, 0, 1);

    for (int tries = 0;; ++tries)
    {
        try
        {
            using (var fileStream = new FileStream(
               path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                // Do Stuff
            }

            break;
        }
        catch (FileNotFoundException)
        {
            // Sometimes the file appears before it is there.
            if (DateTime.Now - started > notFoundLimit)
            {
                // Should be there by now
                break;
            }
        }
        catch (IOException ex)
        {
            // mask in severity, customer, and code
            var hr = (int)(ex.HResult & 0xA000FFFF);
            if (hr != 0x80000020 && hr != 0x80000021)
            {
                // not a share violation or a lock violation
                throw;
            }
        }

        try
        {
            var fi = new FileInfo(path);
            if (fi.Length > lastLength)
            {
                lastLength = fi.Length;
                lastLengthChange = DateTime.Now;
            }
        }
        catch (Exception ex)
        {
        }

        // still locked
        if (DateTime.Now - lastLengthChange > noGrowthLimit)
        {
            // 5 minutes, still locked, no growth.
            break;
        }

        Thread.Sleep(111);
    }

Anda dapat, tentu saja, mengatur waktu Anda sendiri. Kode ini menyisakan cukup waktu selama 5 menit hang. Kode real juga akan memiliki bendera untuk keluar dari utas jika diminta.


1
2017-08-01 21:23



Coba atur filter

myWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;

0
2017-11-25 14:20



Jawaban ini agak terlambat, tetapi jika mungkin saya akan mendapatkan proses sumber untuk menyalin file penanda kecil setelah file besar atau file dan menggunakan FileWatcher pada itu.


0
2018-01-21 21:44