设计解决方案,用于同时处理多个实例化

时间:2015-07-24 17:37:18

标签: c# design-patterns

我不知道我的头衔是否正确。但这就是我想知道的。

我有一个返回某些事件的Download类,并且有几个方法。 Download类的每个实例都可以下载单个文件。所有这些事件和方法都与正在下载的文件有关。

由于它是一个多文件下载程序,当需要下载多个文件时,需要多个实例化。

每个下载都有一个下载ID,但不提供给Download类,以使其独立于其他类。

现在从文件下载的每个实例获取所有信息并能够控制单个下载是问题所在。我如何知道哪个下载是哪个?

任何解决方案?或者你可以推荐的设计模式?我遇到了障碍。

下载课程:

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;

namespace Mackerel_Download_Manager
{
    public class Download
    {
        public event EventHandler<DownloadStatusChangedEventArgs> ResumablityChanged;
        public event EventHandler<DownloadProgressChangedEventArgs> ProgressChanged;
        public event EventHandler Completed;

        public bool stop = true; // by default stop is true
        public bool paused = false;
        SemaphoreSlim pauseLock = new SemaphoreSlim(1);

        string filename;

        public void DownloadFile(Uri DownloadLink, string Path)
        {
            filename = System.IO.Path.GetFileName(Path);

            stop = false; // always set this bool to false, everytime this method is called

            var fileInfo = new FileInfo(Path);
            long existingLength = 0;
            if (fileInfo.Exists)
                existingLength = fileInfo.Length;

            var request = (HttpWebRequest)HttpWebRequest.Create(DownloadLink);
            request.Proxy = null;
            request.AddRange(existingLength);

            try
            {
                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    long fileSize = existingLength + response.ContentLength; //response.ContentLength gives me the size that is remaining to be downloaded
                    bool downloadResumable; // need it for not sending any progress

                    if ((int)response.StatusCode == 206) //same as: response.StatusCode == HttpStatusCode.PartialContent
                    {
                        //Console.WriteLine("Resumable");
                        downloadResumable = true;
                    }
                    else // sometimes a server that supports partial content will lose its ability to send partial content(weird behavior) and thus the download will lose its resumability
                    {
                        if (existingLength > 0)
                        {
                            if (ResumeUnsupportedWarning() == false) // warn and ask for confirmation to continue if the half downloaded file is unresumable
                            {
                                return;
                            }
                        }
                        existingLength = 0;
                        downloadResumable = false;
                    }
                    OnResumabilityChanged(new DownloadStatusChangedEventArgs(downloadResumable));

                    using (var saveFileStream = fileInfo.Open(downloadResumable ? FileMode.Append : FileMode.Create, FileAccess.Write))
                    using (var stream = response.GetResponseStream())
                    {
                        byte[] downBuffer = new byte[4096];
                        int byteSize = 0;
                        long totalReceived = byteSize + existingLength;
                        var sw = Stopwatch.StartNew();
                        while (!stop && (byteSize = stream.Read(downBuffer, 0, downBuffer.Length)) > 0)
                        {
                            saveFileStream.Write(downBuffer, 0, byteSize);
                            totalReceived += byteSize;

                            float currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds;
                            OnProgressChanged(new DownloadProgressChangedEventArgs(totalReceived, fileSize, (long)currentSpeed));

                            pauseLock.Wait();
                            pauseLock.Release();
                        }
                        sw.Stop();
                    }
                }
                if (!stop) OnCompleted(EventArgs.Empty);
            }
            catch (WebException e)
            {
                System.Windows.MessageBox.Show(e.Message, filename);
            }
        }

        public void pause()
        {
            if (!paused)
            {
                paused = true;
                // Note this cannot block for more than a moment
                // since the download thread doesn't keep the lock held
                pauseLock.Wait();
            }
        }

        public void unpause()
        {
            if (paused)
            {
                paused = false;
                pauseLock.Release();
            }
        }

        public void StopDownload()
        {
            stop = true;
            this.unpause();  // stop waiting on lock if needed
        }

        public bool ResumeUnsupportedWarning()
        {
            var messageBoxResult = System.Windows.MessageBox.Show("When trying to resume the download , Mackerel got a response from the server that it doesn't support resuming the download. It's possible that it's a temporary error of the server, and you will be able to resume the file at a later time, but at this time Mackerel can download this file from the beginning.\n\nDo you want to download this file from the beginning?", filename, System.Windows.MessageBoxButton.YesNo);
            if (messageBoxResult == System.Windows.MessageBoxResult.Yes)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        protected virtual void OnResumabilityChanged(DownloadStatusChangedEventArgs e)
        {
            var handler = ResumablityChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnProgressChanged(DownloadProgressChangedEventArgs e)
        {
            var handler = ProgressChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnCompleted(EventArgs e)
        {
            var handler = Completed;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }

    public class DownloadStatusChangedEventArgs : EventArgs
    {
        public DownloadStatusChangedEventArgs(bool canResume)
        {
            ResumeSupported = canResume;
        }
        public bool ResumeSupported { get; private set; }
    }

    public class DownloadProgressChangedEventArgs : EventArgs
    {
        public DownloadProgressChangedEventArgs(long totalReceived, long fileSize, long currentSpeed)
        {
            BytesReceived = totalReceived;
            TotalBytesToReceive = fileSize;
            CurrentSpeed = currentSpeed;
        }

        public long BytesReceived { get; private set; }
        public long TotalBytesToReceive { get; private set; }
        public float ProgressPercentage 
        { 
            get 
            { 
                return ((float)BytesReceived / (float)TotalBytesToReceive) * 100; 
            } 
        }
        public float CurrentSpeed { get; private set; } // in bytes
        public TimeSpan TimeLeft
        {
            get
            {
                var bytesRemainingtoBeReceived = TotalBytesToReceive - BytesReceived;
                return TimeSpan.FromSeconds(bytesRemainingtoBeReceived / CurrentSpeed);
            }
        }
    }
}

下载类在Mackerel类中实例化,开始下载给定的下载。

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;

namespace Mackerel_Download_Manager
{
    public static class Mackerel
    {
        //Main Menu functions
        public static void ResumeDownload(string[] DownloadIDs)
        {
            foreach (var DownloadID in DownloadIDs)
            {
                var itemToResume = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();

                if (itemToResume.Running == false)
                {
                    itemToResume.Running = true;

                    var download = new Download();
                    download.DownloadFile(itemToResume.DownloadLink, itemToResume.SaveTo);
                    var window = new Dialogs.DownloadProgress(itemToResume);
                    window.Show();

                    double progress = 0;
                    itemToResume.Status = string.Format("{0:0.00}%", progress);

                    Downloads.DownloadEntries.CollectionChanged += delegate 
                    { 
                        if (!itemToResume.Running) window.Close(); 
                    };
                }
            }
        }

        public static void StopDownload(string[] DownloadIDs)
        {
            foreach (var DownloadID in DownloadIDs)
            {
                var itemToStop = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
                if (itemToStop.Running == true)
                    itemToStop.Running = false;
            }
        }

        public static void StopAllDownloads()
        {
            foreach (var itemToStop in Downloads.DownloadEntries.Where(download => download.Running == true))
                itemToStop.Running = false;
        }

        public static void RemoveDownload(string[] DownloadIDs) // this method is able to delete multiple downloads
        {
            foreach (var DownloadID in DownloadIDs)
            {
                // delete from the download list
                var selectedDownload = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
                var selectedDownloadIndex = Downloads.DownloadEntries.IndexOf(selectedDownload);
                Downloads.DownloadEntries.RemoveAt(selectedDownloadIndex);
                //delete from the harddrive
                if (File.Exists(selectedDownload.SaveTo))
                    File.Delete(selectedDownload.SaveTo);
            }
            Downloads.Serialize(); // save current state of object
        }

        public static void RemoveCompletedDownloads() // this method just removes all completed downloads from Mackerel's download list (it doesn't delete them from the hard drive)
        {
            foreach (var itemToRemove in Downloads.DownloadEntries.Where(download => download.Status == "Complete").ToList())
            {
                Downloads.DownloadEntries.Remove(itemToRemove);
            }
        }

        // Context Menu
        public static void OpenDownloadProperties(string DownloadID) // Open "Download Properties" for the given download ID
        {
            var DownloadProperties = new Dialogs.Context_Menu.DownloadProperties(DownloadID);
            DownloadProperties.Owner = Application.Current.MainWindow; // so that this dialog centers to its parent window, as its window is set to WindowStartupLocation="CenterOwner"
            DownloadProperties.ShowDialog();
        }
    }
}

完整的源代码在这里:https://github.com/Expenzor/mackerel-download-manager

1 个答案:

答案 0 :(得分:2)

听起来你将下载本身作为一个对象,而是使用“下载器”作为名称。我可能会建议一系列下载的对象或类似的东西。下载器类可以有一个创建新对象的方法 - 可能是下载对象。使用数组或链表等将使您有机会解决对象并独立调用它们 - 知道哪个是。

发布代码也会有所帮助。