Giving out my own code (not 100% polished yet) -- feel free to improve:
using System;
using System.Net;
using System.IO;
namespace HttpDownloader {
class HttpDownloadMain {
const string NumericFormat = "###,###,###,###,###,###,###";
static string CreateFormat(
string preFormat, string placeholder) {
return preFormat.Replace(placeholder, NumericFormat);
}
static string ConvertUrlToFileName(string url) {
string[] terms = url.Split(
new string[] { ":", "//" },
StringSplitOptions.RemoveEmptyEntries);
string fname = terms[terms.Length - 1];
fname = fname.Replace('/', '.');
return fname;
}
static long GetExistingFileLength(string filename) {
if (!File.Exists(filename)) return 0;
FileInfo info = new FileInfo(filename);
return info.Length;
}
static void DownloadOne(
string url, string existingFilename, bool quiet) {
HttpWebRequest webRequest;
HttpWebResponse webResponse;
IWebProxy proxy = null;
string fmt = CreateFormat(
"{0}: {1:#} of {2:#} ({3:g3}%)", "#");
FileStream fs = null;
try {
string fname = existingFilename;
if (fname == null)
fname = ConvertUrlToFileName(url);
webRequest = (HttpWebRequest)WebRequest.Create(url);
long preloadedLength = GetExistingFileLength(fname);
if (preloadedLength > 0)
webRequest.AddRange((int)preloadedLength);
webRequest.Proxy = proxy;
webResponse = (HttpWebResponse)webRequest.GetResponse();
fs = new FileStream(
fname, FileMode.Append, FileAccess.Write);
long fileLength = webResponse.ContentLength;
string todoFormat = CreateFormat(
@"Downloading {0}: {1:#} bytes...", "#");
Console.WriteLine();
Console.WriteLine(todoFormat, url, fileLength);
Console.WriteLine(
"Pre-loaded: preloaded length {0}",
preloadedLength);
Console.WriteLine("Remaining length: {0}", fileLength);
Stream strm = webResponse.GetResponseStream();
int arrSize = 10 * 1024 * 1024;
byte[] barr = new byte[arrSize];
long bytesCounter = preloadedLength;
string fmtPercent = string.Empty;
while (true) {
int actualBytes = strm.Read(barr, 0, arrSize);
if (actualBytes <= 0)
break;
fs.Write(barr, 0, actualBytes);
bytesCounter += actualBytes;
double percent = 0d;
if (fileLength > 0)
percent =
100.0d * bytesCounter /
(preloadedLength + fileLength);
if (!quiet)
Console.WriteLine(
fmt,
fname,
bytesCounter,
preloadedLength + fileLength,
percent);
}
Console.WriteLine(@"{0}: complete!", url);
} catch (Exception e) {
Console.WriteLine(
"{0}: {1} '{2}'",
url, e.GetType().FullName,
e.Message);
} finally {
if (fs != null) {
fs.Flush();
fs.Close();
}
}
}
static void Main(string[] args) {
Console.WriteLine(
"Usage: >HTTPDownloader <http_url> [<output_file_name>]");
if (args.Length < 1) return;
string url = args[0];
string existingFileName = null;
if (args.Length > 1)
existingFileName = args[1];
DownloadOne(url, existingFileName, false);
}
}
}
The code allows to continue downloading partially downloaded files (Important!). It works!
Frankly, the code is still of a prototype quality: hard-coded strings, some other immediate constants, etc., but this way it is all in one file. Also, it is stable and served me well. It can server as as source for derived project (please credit my original).
Do you need FTP as well?