Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

HTTP Library Class

4.50/5 (2 votes)
1 Jul 2013CPOL1 min read 10.8K  
HTTP library class for easy downloading files via HTTP.

Introduction

I recently needed to give an existing application the ability to download files via HTTP from Amazons S3 service for storing files. I found a lot of different sources for doing different bits of the job, but it took some time to collect everything I needed into one working class. I thought it might be a good idea to share this library with others.

Background

One of the main things missing from code samples showing how to download files was any form of verification that the download was successful and no data was lost. I used separate sources to find out how to get the file length from the header of the URL and, if available, how to get the MD5 checksum of the file from the ETag. It took me a bit of time to throw it all together, so I though I might save someone some time if I provide the library for them.

Using the code

You can download a file by simply calling the boolean function DownloadFile() and passing it the URL, the directory to download the file to, the file name with extension and specifying the boolean whether you want to check the hash of the downloaded file, or if not available, rely on the file length for confirming successful download and doing any other checking after. Some example code is:

C#
HTTPLib.HTTP _httpLib = new HTTPLib.HTTP(); 

if (_httpLib.DownloadFile(
      "https://s3-downloadURL/", @"C:\DownloadDest\", 
      "File name with extension", true))
{
	return true; 
}

The file is downloaded with a .PART added after the file extension, and on verifying successful download, the .Part is removed.

You can find the CS file in the attached zip file. Also here is the code in full:

C#
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography;
namespace HTTPLib
{
    class HTTP
    {
        //Download file via http
        public bool DownloadFile(string url, string filePath, string fileName, bool checkHash)
        {
            try
            {
                var partFilePath = String.Format(filePath + "{0}.PART", fileName);
                var downloadedFilePath = String.Format("{0}{1}", filePath, fileName);
                int fileSize = GetFileSize(String.Format("{0}{1}", url, fileName));
                var etag = "";
                var downloaded = 0;
                var percent = 0;
                var last = 0;
                const int BUFFER_SIZE = 16 * 1020;
                if (checkHash)
                {
                    etag = GetETag(String.Format("{0}{1}", url, fileName));
                }
                if (File.Exists(partFilePath))
                {
                    downloaded = (int)new FileInfo(partFilePath).Length;
                    percent = (downloaded * 100) / fileSize;
                }
                if (downloaded == 0 || downloaded >= fileSize)
                {
                    downloaded = 0;
                    using (var outputFileStream = File.Create(partFilePath, BUFFER_SIZE))
                    {
                        var req = WebRequest.Create(new Uri(String.Format("{0}{1}", url, fileName)));
                        using (var response = req.GetResponse())
                        {
                            using (var responseStream = response.GetResponseStream())
                            {
                                var buffer = new byte[BUFFER_SIZE];
                                int bytesRead;
                                do
                                {
                                    bytesRead = responseStream.Read(buffer, 0, BUFFER_SIZE);
                                    outputFileStream.Write(buffer, 0, bytesRead);
                                    downloaded += bytesRead;
                                    percent = (downloaded * 100) / fileSize;
                                    if (percent > last)
                                    {
                                        last = percent;
                                    }
                                }
                                while (bytesRead > 0);
                            }
                        }
                    }
                }
                else
                {
                    using (var _writeStream = 
                      new FileStream(partFilePath, FileMode.Append, FileAccess.Write))
                    {
                        var _readStream = OpenReadStream(downloaded, fileSize, 
                          String.Format("{0}{1}", url, fileName), _writeStream);
                        var buffer = new byte[BUFFER_SIZE];
                        int count = _readStream.Read(buffer, 0, buffer.Length);
                        do
                        {
                            _writeStream.Write(buffer, 0, count);
                            _writeStream.Flush();
                            count = _readStream.Read(buffer, 0, buffer.Length);
                            downloaded += count;
                            percent = (downloaded * 100) / fileSize;
                            if (percent > last)
                            {
                                last = percent;
                            }
                        }
                        while (count > 0);
                    }
                }
                if (File.Exists(partFilePath))
                {
                    var downloadSuccessful = false;
                    if ((int)new FileInfo(partFilePath).Length >= fileSize)
                    {
                        if (checkHash)
                        {
                            var hash = GetMD5Hash(partFilePath);
                            if (hash.ToLower().Equals(etag.ToLower()))
                            {
                                downloadSuccessful = true;
                            }
                        }
                        else
                        {
                            downloadSuccessful = true;
                        }
                        if (downloadSuccessful)
                        {
                            File.Copy(partFilePath, downloadedFilePath, true);
                            //Make sure read only is not set so file can be overwritten if necessary
                            File.SetAttributes(downloadedFilePath, 
                              File.GetAttributes(downloadedFilePath) & ~FileAttributes.ReadOnly);
                            RemovePartFile(partFilePath);
                            return true;
                        }
                        else
                        {
                            //Delete file for fresh try
                            File.Delete(partFilePath);
                        }
                    }
                }
                else
                {
                    //Delete file for fresh try
                    File.Delete(partFilePath);
                }
                return false;
            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                  "Thrown from DownloadFile() -> " + ex.Message, ex);
            }
        }
        /// <summary>
        /// Return size of file to download
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public int GetFileSize(string url)
        {
            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(url));
                req.Method = "HEAD";
                HttpWebResponse resp = (HttpWebResponse)(req.GetResponse());
                int len = (int)resp.ContentLength;
                resp.Close();
                return len;
            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                  "Thrown from GetFileSize() -> " + ex.Message, ex);
            }
        }
        /// <summary>
        /// Return MD5 checksum of file to download from ETag if available
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public string GetETag(string url)
        {
            try
            {
                var etag = "";
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(url));
                req.Method = "HEAD";
                HttpWebResponse resp = (HttpWebResponse)(req.GetResponse());
                etag = resp.Headers.Get("ETag").Replace("\"", "");
                resp.Close();
                return etag;
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Thrown from GetETag() -> " + ex.Message, ex);
            }
        }
        /// <summary>
        /// Creates new stream and sets it's position
        /// to where it can resume an incomplete download
        /// </summary>
        /// <param name="start"></param>
        /// <param name="length"></param>
        /// <param name="url"></param>
        /// <param name="_writeStream"></param>
        /// <returns></returns>
        public Stream OpenReadStream(long start, long length, string url, FileStream _writeStream)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(url));
                request.AddRange((int)start, (int)length);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                if (response.ContentLength == length)
                {
                    _writeStream.Seek(0, SeekOrigin.Begin);
                }
                Stream _readStream;
                _readStream = response.GetResponseStream();
                return _readStream;
            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                  "Thrown from OpenReadStream() -> " + ex.Message, ex);
            }
        }
        public static FileStream GetFileStream(string pathName)
        {
            return (new FileStream(pathName, FileMode.Open,
                      FileAccess.Read, FileShare.ReadWrite));
        }
        public static string GetMD5Hash(string pathName)
        {
            try
            {
            string strResult = "";
            string strHashData = "";
            byte[] arrbytHashValue;
            FileStream oFileStream = null;
            MD5CryptoServiceProvider oMD5Hasher =
                       new MD5CryptoServiceProvider();
            
                oFileStream = GetFileStream(pathName);
                arrbytHashValue = oMD5Hasher.ComputeHash(oFileStream);
                oFileStream.Close();
                strHashData = BitConverter.ToString(arrbytHashValue);
                strHashData = strHashData.Replace("-", "");
                strResult = strHashData;
                
                return (strResult);
            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                  "Thrown from GetMD5Hash() -> " + ex.Message, ex);
            }
        }
        //Remove the part file of the ftp download if it still exists to save space
        private void RemovePartFile(string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    FileInfo fileInfoMP4 = new FileInfo(filePath);
                    long bytesOfMP4 = fileInfoMP4.Length;
                    if (bytesOfMP4 > 0)
                    {
                        File.Delete((filePath));
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                  "Thrown from RemovePartFile() -> " + ex.Message, ex);
            }
        }
    }
} 

You can also use the functions for getting the file size, ETag and hash sum of an existing file separately.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)