PageRenderTime 32ms CodeModel.GetById 2ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/Visual Studio 2008/CSFTPDownload/FTPDownloadClient.cs

#
C# | 294 lines | 195 code | 49 blank | 50 comment | 21 complexity | 7730f336f64fc394e00f46fc28044f52 MD5 | raw file
  1/****************************** Module Header ******************************\
  2* Module Name:  FTPDownloadClient.cs
  3* Project:	    CSFTPDownload
  4* Copyright (c) Microsoft Corporation.
  5* 
  6* This class is used to download files from a FTP server. When the download 
  7* starts, it will download the file in a background thread. The downloaded 
  8* data is stored in a MemoryStream first, and then written to local file.
  9* 
 10* 
 11* This source is subject to the Microsoft Public License.
 12* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
 13* All other rights reserved.
 14* 
 15* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 16* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 17* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 18\***************************************************************************/
 19
 20using System;
 21using System.Collections.Generic;
 22using System.IO;
 23using System.Net;
 24using System.Threading;
 25
 26namespace CSFTPDownload
 27{
 28    public partial class FTPClientManager
 29    {
 30
 31        public class FTPDownloadClient
 32        {
 33            // 2M bytes.
 34            public const int MaxCacheSize = 2097152;
 35
 36            // 2K bytes.
 37            public const int BufferSize = 2048;
 38
 39            FTPClientManager manager;
 40
 41            public event EventHandler<FileDownloadCompletedEventArgs> 
 42                FileDownloadCompleted;
 43
 44            public event EventHandler AllFilesDownloadCompleted;
 45
 46            public FTPDownloadClient(FTPClientManager manager)
 47            {
 48                if (manager == null)
 49                {
 50                    throw new ArgumentNullException("FTPClientManager cannot be null.");
 51                }
 52
 53                this.manager = manager;
 54            }
 55
 56            /// <summary>
 57            /// Download files, directories and their subdirectories.
 58            /// </summary>
 59            public void DownloadDirectoriesAndFiles(IEnumerable<FTPFileSystem> files,
 60                string localPath)
 61            {
 62                if (files == null)
 63                {
 64                    throw new ArgumentNullException(
 65                        "The files to download cannot be null.");
 66                }
 67
 68                // Create a thread to download data.
 69                ParameterizedThreadStart threadStart =
 70                    new ParameterizedThreadStart(StartDownloadDirectoriesAndFiles);
 71                Thread downloadThread = new Thread(threadStart);
 72                downloadThread.IsBackground = true;
 73                downloadThread.Start(new object[] { files, localPath });
 74            }
 75
 76            /// <summary>
 77            /// Download files, directories and their subdirectories.
 78            /// </summary>
 79            void StartDownloadDirectoriesAndFiles(object state)
 80            {
 81                var paras = state as object[];
 82
 83                IEnumerable<FTPFileSystem> files = paras[0] as IEnumerable<FTPFileSystem>;
 84                string localPath = paras[1] as string;
 85
 86                foreach (var file in files)
 87                {
 88                    DownloadDirectoryOrFile(file, localPath);
 89                }
 90
 91                this.OnAllFilesDownloadCompleted(EventArgs.Empty);
 92            }
 93
 94            /// <summary>
 95            /// Download a single file or directory.
 96            /// </summary>
 97            void DownloadDirectoryOrFile(FTPFileSystem fileSystem, string localPath)
 98            {
 99
100                // Download the file directly.
101                if (!fileSystem.IsDirectory)
102                {
103                    DownloadFile(fileSystem, localPath);
104                }
105
106                // Download a directory.
107                else
108                {
109
110                    // Construct the directory Path.
111                    string directoryPath = localPath + "\\" + fileSystem.Name;
112
113                    if (!Directory.Exists(directoryPath))
114                    {
115                        Directory.CreateDirectory(directoryPath);
116                    }
117
118                    // Get the sub directories and files.
119                    var subDirectoriesAndFiles =
120                        this.manager.GetSubDirectoriesAndFiles(fileSystem.Url);
121
122                    // Download the files in the folder and sub directories.
123                    foreach (var subFile in subDirectoriesAndFiles)
124                    {
125                        DownloadDirectoryOrFile(subFile, directoryPath);
126                    }
127                }
128            }
129
130            /// <summary>
131            /// Download a single file directly.
132            /// </summary>
133            void DownloadFile(FTPFileSystem file, string localPath)
134            {
135                if (file.IsDirectory)
136                {
137                    throw new ArgumentException(
138                        "The FTPFileSystem to download is a directory in fact");
139                }
140
141                string destPath = localPath + "\\" + file.Name;
142
143                // Create a request to the file to be  downloaded.
144                FtpWebRequest request = WebRequest.Create(file.Url) as FtpWebRequest;
145
146                request.Credentials = this.manager.Credentials;
147
148                // Download file.
149                request.Method = WebRequestMethods.Ftp.DownloadFile;
150
151                FtpWebResponse response = null;
152                Stream responseStream = null;
153                MemoryStream downloadCache = null;
154
155
156                try
157                {
158
159                    // Retrieve the response from the server and get the response stream.
160                    response = request.GetResponse() as FtpWebResponse;
161
162                    this.manager.OnNewMessageArrived(new NewMessageEventArg
163                    {
164                        NewMessage = response.StatusDescription
165                    });
166
167                    responseStream = response.GetResponseStream();
168
169                    // Cache data in memory.
170                    downloadCache = new MemoryStream(FTPDownloadClient.MaxCacheSize);
171                    byte[] downloadBuffer = new byte[FTPDownloadClient.BufferSize];
172
173                    int bytesSize = 0;
174                    int cachedSize = 0;
175
176                    // Download the file until the download is completed.
177                    while (true)
178                    {
179
180                        // Read a buffer of data from the stream.
181                        bytesSize = responseStream.Read(downloadBuffer, 0, 
182                            downloadBuffer.Length);
183
184                        // If the cache is full, or the download is completed, write 
185                        // the data in cache to local file.
186                        if (bytesSize == 0
187                            || MaxCacheSize < cachedSize + bytesSize)
188                        {
189                            try
190                            {
191                                // Write the data in cache to local file.
192                                WriteCacheToFile(downloadCache, destPath, cachedSize);
193
194                                // Stop downloading the file if the download is paused, 
195                                // canceled or completed. 
196                                if (bytesSize == 0)
197                                {
198                                    break;
199                                }
200
201                                // Reset cache.
202                                downloadCache.Seek(0, SeekOrigin.Begin);
203                                cachedSize = 0;
204                            }
205                            catch (Exception ex)
206                            {
207                                string msg = string.Format(
208                                    "There is an error while downloading {0}. "
209                                    + " See InnerException for detailed error. ", 
210                                    file.Url);
211                                ApplicationException errorException 
212                                    = new ApplicationException(msg, ex);
213
214                                // Fire the DownloadCompleted event with the error.
215                                ErrorEventArgs e = new ErrorEventArgs
216                                {
217                                    ErrorException = errorException
218                                };
219
220                                this.manager.OnErrorOccurred(e);
221
222                                return;
223                            }
224
225                        }
226
227                        // Write the data from the buffer to the cache in memory.
228                        downloadCache.Write(downloadBuffer, 0, bytesSize);
229                        cachedSize += bytesSize;
230                    }
231
232                    var fileDownloadCompletedEventArgs = new FileDownloadCompletedEventArgs
233                    {
234
235                        LocalFile = new FileInfo(destPath),
236                        ServerPath = file.Url
237                    };
238
239                    this.OnFileDownloadCompleted(fileDownloadCompletedEventArgs);
240                }
241                finally
242                {
243                    if (response != null)
244                    {
245                        response.Close();
246                    }
247
248                    if (responseStream != null)
249                    {
250                        responseStream.Close();
251                    }
252
253                    if (downloadCache != null)
254                    {
255                        downloadCache.Close();
256                    }
257                }
258            }
259
260            /// <summary>
261            /// Write the data in cache to local file.
262            /// </summary>
263            void WriteCacheToFile(MemoryStream downloadCache, string downloadPath,
264                int cachedSize)
265            {
266                using (FileStream fileStream = new FileStream(downloadPath, 
267                    FileMode.Append))
268                {
269                    byte[] cacheContent = new byte[cachedSize];
270                    downloadCache.Seek(0, SeekOrigin.Begin);
271                    downloadCache.Read(cacheContent, 0, cachedSize);
272                    fileStream.Write(cacheContent, 0, cachedSize);
273                }
274            }
275
276            protected virtual void OnFileDownloadCompleted(FileDownloadCompletedEventArgs e)
277            {
278
279                if (FileDownloadCompleted != null)
280                {
281                    FileDownloadCompleted(this, e);
282                }
283            }
284
285            protected virtual void OnAllFilesDownloadCompleted(EventArgs e)
286            {
287                if (AllFilesDownloadCompleted != null)
288                {
289                    AllFilesDownloadCompleted(this, e);
290                }
291            }
292        }
293    }
294}