【问题标题】:Continuously download latest changes from a file从文件中不断下载最新的更改
【发布时间】:2011-01-17 21:09:39
【问题描述】:

我什至不知道如何开始。除了我曾经遵循的下载简单文本文件的教程之外,我对下载文件几乎一无所知。

请允许我解释一下我应该下载什么样的文件。我有一个记录本地演示的游戏。该文件通过添加所谓的帧而不断增长。 我们试图实现的是让这个文件完全下载,并且一旦下载它就只获取该文件的最新添加,而不是再次获取整个文件。这允许我们在远程系统上播放仍在创建的演示。

我们已经使用 wget 成功地做到了这一点,但我们想围绕下载机制编写一个用户友好的客户端。因此,wget 所做的是检查文件是否已更改,然后仅获取最后添加的字节。该文件增长约 40KBps。通过这种方式,我们可以轻松地将流设置到远程系统。

不能一直重新下载整个文件。我们设法检查了在线文件是否已更改,但是当它检测到更改时,它只是下载了整个文件。这些文件最终会增长到 15Mb,因为这个大小,我们无法真正提供快速下载并跳到游戏中的当前帧。

源代码、教程,甚至只是下载代码以及其工作原理的简单说明,都会对我们的项目有很大帮助。

提前致谢。

【问题讨论】:

  • 您如何访问该文件? HTTP? Tcp/IP 流,其他?这只能是客户端还是您可以进行服务器端更改?

标签: c# download


【解决方案1】:

简单的实现

  • 执行 HEAD 请求
  • 获取内容长度
  • 使用字节范围来请求文件的新部分(通过比较本地长度和内容长度 - 就像下载管理器恢复功能一样)
  • 将其附加到您的本地文件中

完成。

【讨论】:

  • 我的印象是这是一个本地文件 (I have a game that records a local demo)。所以我不认为 HTTP 标头是正确的方法。虽然我不同意这是针对网络文件的解决方案,但这似乎不是 this question's 解决方案。
  • 我理解他是一个文件,他想下载并将其流式传输到他自己的本地游戏中。所以无论是我还是你都错过了这个问题,我想我们会等待 OP 的回答 :)
【解决方案2】:

假设您使用 HTTP 请求来检索您的文件,以下内容应该对您有所帮助。我利用最后修改和内容长度值并轮询文件以进行更改。如果您触摸文件并且不进行更新,或者如果 ehder 出于任何原因可能会更改,您可能希望更改它,这不是查找更改的好方法。但是,这应该会让您朝着正确的方向前进。

如果你真的有动力,你可以将轮询代码放在我在程序中使用的线程中,创建一个“FileUpdatedEventArgs”类并通过事件将更改传回。 - 或者,也许你只是坚持自己投票。 ;-)

public class SegmentedDownloader
{
    #region Class Variables

    /// <summary>
    /// Date the file was last updated
    /// Used to compare the header file for changes since
    /// </summary>
    protected DateTime LastModifiedSince = default(DateTime);

    /// <summary>
    /// Length of the file when it was last downlaoded
    /// (this will be used to provide a content offset on next download)
    /// </summary>
    protected Int64 ContentLength = default(Int64);

    /// <summary>
    /// The file we're polling
    /// </summary>
    protected Uri FileLocation;

    #endregion

    #region Construct

    /// <summary>
    /// Create a new downloader pointing to the specific file location
    /// </summary>
    /// <param name="URL">URL of the file</param>
    public SegmentedDownloader(String URL)
        : this(new Uri(URL))
    {
    }

    public SegmentedDownloader(Uri URL)
    {
        this.FileLocation = URL;
    }

    #endregion

    /// <summary>
    /// Grab the latests details from the page
    /// </summary>
    /// <returns>Stream with the changes</returns>
    public Stream GetLatest()
    {
        Stream result = null;

        try
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(this.FileLocation);
            if (this.ContentLength > 0)
                webRequest.AddRange(this.ContentLength);
            webRequest.IfModifiedSince = this.LastModifiedSince;

            HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

            Int64 contentLength = webResponse.ContentLength;
            DateTime lastModifiedSince = Convert.ToDateTime(webResponse.Headers["Last-Modified"]);
            if (contentLength > 0 || lastModifiedSince.CompareTo(this.LastModifiedSince) > 0)
            {
                result = webResponse.GetResponseStream();
                this.ContentLength += contentLength;
                this.LastModifiedSince = lastModifiedSince;
            }
        }
        //catch (System.Net.WebException wex)
        //{ // 302 = unchanged
        //    Console.WriteLine("Unchanged");
        //}
        catch (Exception)
        {
            result = null;
        }
        return result;
    }
}

可以以这种方式使用:

class Program
{
    static TimeSpan updateInterval = TimeSpan.FromSeconds(5);
    static Thread tWorker;
    static ManualResetEvent tReset;

    static void Main(string[] args)
    {
        tReset = new ManualResetEvent(false);
        tWorker = new Thread(new ThreadStart(PollForUpdates));
        tWorker.Start();

        Console.Title = "Press ENTER to stop";
        Console.ReadLine();

        tReset.Set();
        tWorker.Join();
    }

    static void PollForUpdates()
    {
        SegmentedDownloader segDL = new SegmentedDownloader("http://localhost/dataFile.txt");
        do
        {
            Stream fileData = segDL.GetLatest();
            if (fileData != null)
            {
                using (StreamReader fileReader = new StreamReader(fileData))
                {
                    if (fileReader.Peek() > 0)
                    {
                        do
                        {
                            Console.WriteLine(fileReader.ReadLine());
                        }
                        while (!fileReader.EndOfStream);
                    }
                }
            }
        }
        while (!tReset.WaitOne(updateInterval));
    }
}

【讨论】:

    【解决方案3】:

    首先,不要将其视为文件。听起来您正在处理的是随着时间的推移修改数据包的流。如果本地计算机上的基本文件丢失或过期,那么您需要进行一些额外的下载来建立基本文件,但是一旦到位,大部分时间将使用的代码是接收更新帧并应用通过附加或覆盖原始数据,将它们添加到基础。

    将其视为版本控制系统可能会有所帮助,或者至少熟悉版本控制术语并使用类似概念处理您的解决方案。比如:每个版本都有一个唯一的签名(通常是实际数据的散列或摘要),并且有一个与签名相关的顺序。如果您可以要求客户端始终按顺序获取更新帧(如果客户端还没有帧 150-199,则不允许客户端具有帧 200),那么客户端对服务器的请求可以简单地是“我有xyz。我需要了解什么是最新的?”

    如果您的数据更改发生得很快,特别是如果它们是多个客户端同时处理共享文档的结果,那么单独使用时间签名可能不够独特,无法可靠。时间签名 + 内容的数字哈希可能是一个好主意,我相信大多数版本控制系统以某种方式使用它。

    考虑使用版本控制系统作为您的核心实现并围绕它构建您的服务器和客户端而不是自己编写可能也是值得的。如果您对应用程序有任何要求以允许用户“返回”到以前的修订版或获取当前文档并进行其他人看不到的私人更改(版本控制术语中的私人分支),这将特别有用。

    【讨论】:

      【解决方案4】:

      好的,我已阅读此处提出的一些额外问题: 我们正在通过 HTTP 访问该文件。它被推送到网络服务器上。推送机制是我们以后可以处理的。现在我们正在使用丑陋的批处理文件来启动程序并不断检查本地更改,但这对用户来说是不可见的,所以以后会担心。

      对于其他 2 个问题,大致是这样的。 连接到游戏服务器的游戏在本地录制演示 -> 将此演示上传到网络空间 -> 应将此演示下载给可以播放演示的本地用户。

      【讨论】:

      • 我现在发布了一个解决方案,因为有些事情已经被清除了。看看这是否有助于或至少将您指向正确的位置。
      猜你喜欢
      • 2021-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-03
      • 1970-01-01
      • 2013-05-19
      相关资源
      最近更新 更多