Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 文件系统监视程序丢失文件_C#_Xml_Filesystemwatcher - Fatal编程技术网

C# 文件系统监视程序丢失文件

C# 文件系统监视程序丢失文件,c#,xml,filesystemwatcher,C#,Xml,Filesystemwatcher,我是c#的新手,正在编写一个程序,使用从folderWatch方法调用的fileSystemWatcher来监视文件夹中的.xml文件。xml文件包含一个电子邮件地址和一个指向图像的路径,一旦读取就会通过电子邮件发送。如果我一次只添加几个xml,那么我的代码可以很好地工作,但是当我试图将大量xml转储到文件夹fileSystemWatcher中时,并没有处理所有xml。请帮我指出正确的方向 private System.IO.FileSystemWatcher m_Watcher; public

我是c#的新手,正在编写一个程序,使用从folderWatch方法调用的fileSystemWatcher来监视文件夹中的.xml文件。xml文件包含一个电子邮件地址和一个指向图像的路径,一旦读取就会通过电子邮件发送。如果我一次只添加几个xml,那么我的代码可以很好地工作,但是当我试图将大量xml转储到文件夹fileSystemWatcher中时,并没有处理所有xml。请帮我指出正确的方向

private System.IO.FileSystemWatcher m_Watcher;
public string folderMonitorPath = Properties.Settings.Default.monitorFolder;

    public void folderWatch()
    {
        if(folderMonitorPath != "")
        {
            m_Watcher = new System.IO.FileSystemWatcher();
            m_Watcher.Filter = "*.xml*";
            m_Watcher.Path = folderMonitorPath;
            m_Watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                                     | NotifyFilters.FileName | NotifyFilters.DirectoryName;
            m_Watcher.Created += new FileSystemEventHandler(OnChanged);
            m_Watcher.EnableRaisingEvents = true;
        }
    }

    public void OnChanged(object sender, FileSystemEventArgs e)
    {
        displayText("File Added " + e.FullPath);
        xmlRead(e.FullPath);
    }
读取xml

    public void xmlRead(string path)
    {

        XDocument document = XDocument.Load(path);
        var photo_information = from r in document.Descendants("photo_information")
                                select new
                                {
                                    user_data = r.Element("user_data").Value,
                                    photos = r.Element("photos").Element("photo").Value,
                                };
        foreach (var r in photo_information)
        {
            if (r.user_data != "")
            {
                var attachmentFilename = folderMonitorPath + @"\" + r.photos;
                displayText("new user data " + r.user_data);
                displayText("attemting to send mail");
                sendemail(r.user_data, attachmentFilename);
            }
            else
            {
                displayText("no user data moving to next file");
            }
        }
寄信

public void sendemail(string email, string attachmentFilename)
    {
        //myTimer.Stop();

            MailMessage mail = new MailMessage();
            SmtpClient SmtpServer = new SmtpClient(smtpClient);

            mail.From = new MailAddress(mailFrom);
            mail.To.Add(email);
            mail.Subject = "test";
            mail.Body = "text";

            SmtpServer.Port = smtpPort;
        SmtpServer.Credentials = new System.Net.NetworkCredential("username", "password");
        SmtpServer.EnableSsl = true;
        // SmtpServer.UseDefaultCredentials = true;

        if (attachmentFilename != null)
            {
                Attachment attachment = new Attachment(attachmentFilename, MediaTypeNames.Application.Octet);
                ContentDisposition disposition = attachment.ContentDisposition;
                disposition.CreationDate = File.GetCreationTime(attachmentFilename);
                disposition.ModificationDate = File.GetLastWriteTime(attachmentFilename);
                disposition.ReadDate = File.GetLastAccessTime(attachmentFilename);
                disposition.FileName = Path.GetFileName(attachmentFilename);
                disposition.Size = new FileInfo(attachmentFilename).Length;
                disposition.DispositionType = DispositionTypeNames.Attachment;
                mail.Attachments.Add(attachment);
            }
        try
        {
            SmtpServer.Send(mail);
            displayText("mail sent");
        }
        catch (Exception ex)
        {
           displayText(ex.Message);

        }

    }

首先,
FileSystemWatcher
对存储挂起的通知进行了内部限制。根据文件:

系统通知组件文件更改,并存储这些更改 组件创建并传递给API的缓冲区中的更改。每个 事件最多可使用16个字节的内存,不包括文件名。 如果在短时间内有许多更改,缓冲区可能会溢出。 这会导致组件无法跟踪目录中的更改

您可以通过将
InternalBufferSize
设置为
64*1024
(64KB,最大允许值)来增加该缓冲区

接下来(也许更重要)是如何清除缓冲区。您的
OnChanged
处理程序将被调用,并且仅当它完成时-通知将从该缓冲区中删除。这意味着,如果您在处理程序中做了大量工作,缓冲区溢出的可能性会大得多。为了避免这种情况,请在
OnChanged
处理程序中尽可能少做一些工作,并在单独的线程中完成所有繁重的工作,例如(不是生产准备好的代码,只是为了错觉):

var queue=new BlockingCollection(new ConcurrentQueue());
新线程(()=>{
foreach(队列中的变量项。GetConsumingEnumerable()){
//用物品做重物
}
}) {
IsBackground=true
}.Start();
var w=new FileSystemWatcher();
//其他东西
w、 已更改+=(发件人,参数)=>
{
//不需要花费时间,因此溢出机会大大减少
添加(args.FullPath);
};

您也没有订阅
FileSystemWatcher
Error
事件,因此您不知道何时(以及是否)出现问题。

我学到了一个艰难的方法,如果您必须使用可靠的文件监视器,请使用

如果您有足够的权限,可以通过以下方式访问.NET:


您也可以使用flie Length+LastModifiedDate通过计时器轮询手动实现此功能。

FSW的文档警告,如果事件处理时间过长,某些事件可能会丢失。这就是为什么它总是与队列和/或后台处理一起使用

一个选项是使用Task.Run在后台执行处理:

public void OnChanged(object sender, FileSystemEventArgs e)
{
    _logger.Info("File Added " + e.FullPath);
    Task.Run(()=>xmlRead(e.FullPath));
}
请注意,我使用日志记录而不是
displayText
所做的任何事情。无法从其他线程访问UI线程。如果要记录进度,请使用日志库

您还可以使用
IProgress
界面来报告长时间运行的作业的进度,或者您希望通过它发布的任何其他内容。
Progress
实现负责将Progress对象封送到它的父线程,通常是UI线程

更好的解决方案是使用。ActionBlock有一个输入缓冲区,可以对传入消息进行排队,还有一个DOP设置,允许您指定可以同时执行多少操作。默认值为1:

ActionBlock<string> _mailerBlock;

public void Init()
{
    var options=new ExecutionDataflowBlockOptions { 
        MaxDegreeOfParallelism = 5
     };
    _mailerBlock = new ActionBlock<string>(path=>xlmRead(path),options);
}

public void OnChanged(object sender, FileSystemEventArgs e)
{
    _logger.Info("File Added " + e.FullPath);
    _mailerBlock.Post(e.FullPath);
} 
sendmail
发送至:

public void sendMailFromInfo(EmailInfo info)
{
    string email=info.Data;
    string attachmentFilename=info.Attachment;
}
当您想要终止管道时,在head块上调用
Complete()
,并等待尾部的完成。这将确保处理所有剩余文件:

readerBlock.Complete();
await mailerBlock.Completion;

它很可能会因为花了大量时间来编写代码而丢失这些代码-将其线程化并有一个文件队列您必须使用错误事件让FSW告诉您您做错了。FSW非常容易出错。由于某些文件系统事件,它将随机停止侦听(没有任何错误通信)。如果有兴趣的话,我有一个可以让它更容易可靠地使用的工具。修改日期本身是不可靠和缓慢的。如果你不滥用FSW,它就可以正常工作。并且.NET无法访问日志,除非您使用AlphaFS这样的库并拥有管理员权限以便在整个卷中启用它。您还没有意识到,使用轮询更轻松,使用上次修改的日期+长度就足以知道在大多数情况下文件是否已更改。如果需要超高精度,可以使用文件流的前几位或最后几位的md5哈希。你只需要知道如何阅读日记。不需要一些巨大的库。谢谢Evk经过一点努力,我让它工作起来了,似乎文件不再丢失了。@Evk我有一个类似的情况,我的代码中的更改看起来像
w.Changed+=(sender,args)=>{//这里我调用了一个函数,它包含4个参数,性能(string,string,string,int);//那么有没有一种方法可以存储所有4个参数,这样我就可以从单独的thread.queue.Add(args.FullPath);}
@m_alpha您可以创建具有4个属性(PerformAction的参数)的单独类,并将该类的实例存储在队列中,而不仅仅是一个字符串。@Evk感谢您的回答,我昨天也做了类似的事情。创建了一个结构,然后将其实例存储在队列中而不是类中。想询问是否可以在事件处理程序本身中调用
新线程(()=>{foreach(queue.getconsumineGenumerable()中的var item){//使用item}
,即
w.Changed+=(发送方,参数)
@m_alpha在这种情况下,您将在每次更改时创建一个新线程,因此,如果有100个更改,则将有100个线程执行相同的操作。您应该在处理程序之外执行此操作。
public IEnumerable<EmailInfo> infosFromXml(string path)
{
    // Same as before ...
    foreach (var r in photo_information)
    {
        if (r.user_data != "")
        {
            ...
            yield return new EmailInfo{
                      Data=r.user_data, 
                      Attachment=attachmentFilename};
        }
       ...
    }
}
public void sendMailFromInfo(EmailInfo info)
{
    string email=info.Data;
    string attachmentFilename=info.Attachment;
}
readerBlock.Complete();
await mailerBlock.Completion;