.NET断点下载

场景:最近由于公司项目中需要使用到断点续传这一功能,并且需要将文件和服务器的文件进行,只好自己去学习和了解一些有关断点续传的知识,其中,主要需要学习的是HTTP相关的知识,比如Header,其实这里面的Range属性就是用来实现断点下载的,下面直接贴代码:

其中服务器端的代码主要是使用ASP.NET MVC写的,如下所示:

/// <summary>  
/// 下载的方法  
/// </summary>  
/// <param name="filename">下载文件的路径</param>  

public ActionResult Reget(string filename)
{
	long start = 0L, end = 0L;
	Stream iStream = null;
	var result = GetMD5(filename, out string message);
	// 分块读取,每块512 bytes   
	byte[] buffer = new Byte[512];
	//文件的大小  
	int length;
	// 文件的字节数  
	long dataToRead;

	// 得到服务器上面的文件
	var path = Server.MapPath("/Files/Files/");
	string filepath = path + filename;

	try
	{
		Response.Clear();
		Response.ClearHeaders();

		//打开文件  
		iStream = new FileStream(filepath, FileMode.Open,
			FileAccess.Read, FileShare.Read);

		// 总字节  
		dataToRead = iStream.Length;

		if (Request.Headers["Range"] != null)
		{
			//如果是续传请求,则获取续传的起始位置,即已经下载到客户端的字节数  
			Response.StatusCode = 206;
			//重要:续传必须,表示局部范围响应。  
			var range = Request.Headers["Range"].ToString();
			var startRange = range.Substring(6);
			start = Convert.ToInt64(startRange.Substring(0, startRange.LastIndexOf("-")));
			end = Convert.ToInt64(range.Substring(startIndex: range.LastIndexOf("-") + 1));

		}
		//不是从最开始下载,  
		////响应的格式是:Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]  
		Response.AddHeader("Range", "bytes=" + start + "-" + end);
		Response.AddHeader("Connection", "Keep-Alive");
		Response.ContentType = "application/octet-stream";   //MIME类型:匹配任意文件类型   
		Response.ContentType = "application/octet-stream";
		Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(filename));
		Response.AddHeader("Content-Length", string.Format("{0}", iStream.Length));
		Response.AddHeader("Content-MD5", message);//用于验证文件
		iStream.Position = start;
		dataToRead = end - start;
		// 读字节  
		while (dataToRead > 0)
		{
			//确认客户端连接  
			if (Response.IsClientConnected)
			{
				// Read the data in buffer.  
				length = iStream.Read(buffer, 0, (int)dataToRead);
				// Write the data to the current output stream.  
				Response.OutputStream.Write(buffer, 0, length);
				// Flush the data to the HTML output.  
				Response.Flush();
				buffer = new Byte[length];
				dataToRead = dataToRead - length;
			}
			else
			{
				//prevent infinite loop if user disconnects  
				dataToRead = -1;
			}
		}

	}
	catch (Exception ex)
	{
		// Trap the error, if any.  
		return Content("Error : " + ex.Message);
	}
	finally
	{
		if (iStream != null)
		{
			//关闭流  
			iStream.Close();
		}
		Response.Close();
	}
	return new EmptyResult();
}

接下来就展示客户端的代码,并且对文件进行MD5校验:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DownloadTaskDemo
{
	class Program
	{
		static void Main(string[] args)
		{
			//1、查看文件是否已下载过和配置文件是否已生成
			//2、已下载或已生成文件,直接读取它们
			//3、否则,从零开始下载,并生成配置文件
			//4、通过对请求头的Range属性进行配置来获取文件某一段内容,服务器并返回内容,同时将已下载的部分记录在配置文件中
			//5、判断文件是否下载完成,下载完成就根据服务器返回的MD5进行校验,并记录到配置文件
			long start = 0L, end = 0L;
			string fn = "";
			long contentLength = 0L;
			string md5 = "";
			var isComplate = false;
			string StrFileName = @"d:\my.tmp";
			string StrUrl = "http://localhost:56159/Upload/Reget?filename=f512b5fa8cab41b9e8940107ca657862.jar";
			// StrUrl = "http://localhost:56159/Upload/Reget?filename=Paper20180412.docx";
			//打开上次下载的文件或新建文件
			long lStartPos = 0;
			FileStream fs;
			if (File.Exists(StrFileName))
			{
				fs = File.OpenWrite(StrFileName);
				lStartPos = fs.Length;
				fs.Seek(lStartPos, SeekOrigin.Current);   //移动文件流中的当前指针
			}
			else
			{
				fs = new FileStream(StrFileName, FileMode.Create);
				lStartPos = 0;
			}
			//打开网络连接
			try
			{
				System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(StrUrl);
				request.AddRange(lStartPos, lStartPos + 512);
				Stream ns;
				//向服务器请求,获得服务器回应数据流
				int i = 1;
				while (!isComplate)
				{
					i++;
					if (start != 0 && end != 0 && contentLength != 0)
					{
						request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(StrUrl);
						//矫正range
						if (end > contentLength)
						{
							request.AddRange(start, contentLength);
						}
						else
						{
							request.AddRange(start, end);
						}
					}
					var response = request.GetResponse();
					if (response.Headers["Content-Length"] != null)
					{
						contentLength = Convert.ToInt64(response.Headers["Content-Length"]);
					}
					md5 = response.Headers["Content-MD5"];
					Console.WriteLine(md5);
					if (response.Headers["Range"] != null)
					{
						var range = response.Headers["Range"].ToString();
						var startRange = range.Substring(6);
						start = Convert.ToInt64(startRange.Substring(0, startRange.LastIndexOf("-")));
						end = Convert.ToInt64(range.Substring(startIndex: range.LastIndexOf("-") + 1));
						Console.WriteLine(start + "-" + end);
					}
					var cd = response.Headers["Content-Disposition"];
					fn = cd.Substring(cd.LastIndexOf("=") + 1);
					ns = response.GetResponseStream();
					byte[] nbytes = new byte[512];
					int nReadSize = 0;
					nReadSize = ns.Read(nbytes, 0, 512);
					while (nReadSize > 0)
					{
						//同时向同一个记录里面取数据  并将数据从相同位置写
						fs.Write(nbytes, 0, nReadSize);
						nReadSize = ns.Read(nbytes, 0, 512);
					}
					//下载完成就退出
					if (end == contentLength)
					{
						isComplate = true;
						break;
					}
					start = end;
					end = end + 512;
					//获取剩余512字节的数据
					ns.Close();
				}

				fs.Close();
				var valMd5 = GetMD5(filePath: StrFileName);
				Console.WriteLine(valMd5);
				if (md5.Equals(valMd5))
				{
					Console.WriteLine("校验成功");
				}
				else
				{
					Console.WriteLine("校验失败");
				}
				File.Move(StrFileName, @"d:\" + fn);
				Console.WriteLine("下载完成");
			}
			catch (Exception ex)
			{
				fs.Close();
				Console.WriteLine("下载过程中出现错误:" + ex.Message);
			}
			Console.Read();
		}

		/// <summary>
		/// MD5校验
		/// </summary>
		/// <param name="fileName"></param>
		/// <param name="message"></param>
		/// <returns></returns>
		public static string GetMD5(string filePath)
		{
			string path = filePath;
			string message = "";
			try
			{
				FileStream file = new FileStream(path, System.IO.FileMode.Open);
				MD5 md5 = new MD5CryptoServiceProvider();
				byte[] retVal = md5.ComputeHash(file);
				file.Close();
				string result = BitConverter.ToString(retVal);
				message = result.Replace("-", "");
				return message;
			}
			catch (Exception ex)
			{
				message = "错误信息" + ex.Message;
				return message;
			}
		}

	}
}

在这里,主要涉及的知识有HTTP的相关知识可以参考:https://en.wikipedia.org/wiki/List_of_HTTP_header_fields

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值