繁体   English   中英

如何通过经常使用的网络复制文件?

[英]How to copy a file over the network that is in frequent use?

我需要通过网络复制一个大文件(大约20 MB)。 通常情况下,这不是问题,但是,文件被与文件位于同一个框中的应用程序相当频繁地写入(大约每秒一次)。

因为文件经常被写入, File.Copy调用经常失败。 我也尝试过File.ReadAllLines ,它似乎每次都有效,但需要永远。

是否有更好,更有效,更安全的方式通过网络复制文件?

PS该文件由使用Log4Net的进程写入。 并且,如果有人想知道,写作的过程不在我手中。

您可以在打开文件时使用fileshare字段以允许其他进程在访问它时访问它,不幸的是,如果您的进程不是锁定文件的进程,则无法释放它并且您已经提到过您不能更改执行写入的代码,通过允许文件共享字段中的r / w,您可以允许其他进程在复制时访问该文件。 你可以编写一个小函数来检查文件是否被锁定并更改你的代码,这样当它被锁定时你会继续检查直到它被解锁然后复制它。

File.Copy可能无法获取文件的独占锁。

考虑到即使在网络上, File.Move也是一种快速操作。 如果该文件不需要保留在以前的应用程序中,则可以运行

for (int i = 0; i < 10; i++)
{
    try {
        File.Move(logFilePath, tempPathOnRemoteServer);
    } catch (Exception) { 
        if (i == 9)
            throw new Exception("Could not acquire lock");
        System.Threading.Thread.Sleep(1000 * (i+1)); }
}
File.Copy(tempPathOnRemoteServer, endPathOnLocalHost);

假设日志记录应用程序可以从打开时正在移动的文件中恢复,这应该没有问题。

或者,如果无法移动文件,请使用与上面相同的代码,将File.Move替换为File.Copy,但如果副本耗时太长,则排他锁可能会导致其他应用程序出现问题。

使用FileStream类读取文件,不要忘记在重载的构造函数中为FileAccess和FileShare设置适当的值。

FileStream(String, FileMode, FileAccess, FileShare)

你可以尝试处理它:

string pathToFile = "path-to-your-file" ;
using ( Stream     s = File.Open( pathToFile , FileMode.Open , FileAccess.Read , FileShare.ReadWrite ) )
using ( TextReader r = new StreamReader( s ) )
{
    string fileContents = r.ReadToEnd() ;
    process( fileContents ) ;
}

如果写入文件的进程不愿意共享它以供阅读,那么你就是SOL。

但是。 你说:

PS该文件由使用Log4Net的进程写入。 并且,如果有人想知道,写作的过程不在我手中。

您尝试访问的文件是由Log4Net Appender生成的,如FileAppender还是RollingFileAppender

如果是这样,默认的日志记录模型是FileAppender.ExclusiveLock ,它通过独占锁保持文件打开持续时间,但是IIRC,它仍然愿意共享它以供读取。

但更简单(为什么重新发明轮子?)获得所需结果(通过网络发送文件)的方法是使用Log4Net配置 您可以将一个appender添加到源应用程序Log4Net配置文件中,它将对您有所帮助。 你可以使用:

  • RemotingAppender “旨在将事件传递到远程接收器。这是实现RemotingAppender.IRemoteLoggingSink接口的任何对象。它使用.NET远程处理来传递事件。通过设置appenders Sink属性来指定传递事件的对象。”

  • TelnetAppender “接受套接字连接并将日志消息流回客户端。输出以telnet友好的方式提供,以便可以通过TCP / IP套接字监视日志。这允许简单地远程监视应用程序日志记录。端口是23(telnet端口)。“

无论哪种方式,您只需要编写一个处理入站日志消息的简单服务。

就效率而言,似乎如果它是log4net输出,你只需要跟踪高水位标记并复制新的东西。

  1. 打开源文件以进行读取
  2. 创建目标文件
  3. 将数据复制到整个网络的文件末尾
  4. 保存流中的当前位置
  5. 睡觉
  6. 打开源文件以进行读取
  7. 打开要追加的目标文件
  8. 寻求在流中记住的位置(可能不再在文件末尾)
  9. 通过网络复制数据直到文件结尾,附加到目标文件。
  10. 来自#4的重复, 令人作呕

只是一个想法。

如果您无法修改写入该文件的程序,那么您的选项会受到一些限制。 想到的两种可能性是:

打开文件独占,并将其复制到循环中:

using (var fin = File.Open(InputFilename, FileMode.Open, FileAccess.Read, FileShare.None))
{
    using (var fout = File.OpenWrite(OutputFilename))`
    {
        int bytesRead = 0;
        var buffer = new byte[65536];
        while ((bytesRead = fin.Read(buffer, 0, buffer.Length)) > 0)
        {
            fout.Write(buffer, 0, bytesRead);
        }
    }
}

如果在尝试打开文件进行读取时,如果Log4Net打开文件进行写入,则必须编写代码来处理引发的异常。 该代码应该等待片刻然后重试。

这里的潜在缺点是,如果Log4Net无法写入文件(因为它打开以进行独占访问),它可能会抛出异常。 如果Log4Net不喜欢你打开文件进行独占访问,那么......

只需重命名该文件,然后随意复制即可。 如果在尝试重命名时Log4Net已打开文件,则代码将引发异常。 你可以重试一下。 如果Log4Net在重命名后尝试写入该文件,Log4Net将创建一个具有该名称的新文件。

我没有看到任何其他选择。

顺便说一句,20 MB并不是一个大文件。 在现代网络速度下,您应该能够在一秒钟内完成复制。

好的,那么,另一种选择是始终模拟地写入两个文件。 一个 - 真实的,另一个,临时名称(让我们说GUID)。 只要您需要复制,请立即停止写入临时文件(而是创建一个新文件,然后继续写入)。 然后该文件将不再被锁定,因此您可以根据需要上传它。 仅在 - 删除它之后。

那么首先在本地制作一个副本 - 这将非常快并将保留它的状态,然后上传副本,最后删除副本?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM