C#文件断点续传实现方法

C#文件断点续传实现方法攻略

背景和原理介绍

在文件传输中,当传输过程中出现异常,或连接中断,一般需要重新传输。但如果文件太大,重新传输的成本太高,而此时恰好传输过程中已经传输了一部分,通过断点续传可以只续传未完成的部分,可以大幅减少传输成本。文件断点续传实现的原理,在下载时是通过请求服务端时在header部分加上range请求头,标明下载文件的起止断点,服务端则返回此范围内的文件内容。而上传时,可以通过multipart/form-data的方式上传文件分块,并在服务端将分块拼接起来即可实现。

文件下载断点续传实现

方式一:使用HttpWebRequest发送range请求头

在下载文件时,使用HttpWebRequest发送range请求头即可实现文件下载断点续传。具体实现步骤如下:
1. 使用HttpWebRequest构建请求对象
csharp
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);

2. 使用AddHeader方法添加range请求头,可根据实际需要设置请求范围,如果不设置,则表示从0字节开始下载
csharp
request.AddRange(startByte, endByte);

3. 发送请求并返回响应结果
csharp
HttpWebResponse response = (HttpWebResponse) request.GetResponse();

4. 读取响应数据流,并将数据写入本地文件中
csharp
using (Stream responseStream = response.GetResponseStream())
{
using (FileStream fs = new FileStream(filePath, FileMode.Append))
{
byte[] buffer = new byte[1024];
int length = 0;
while ((length = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, length);
}
}
}

实际代码示例可参见如下代码:
```csharp
public static void DownloadFile(string url, string filePath)
{
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
long fileSize = GetFileSize(url);
request.Timeout = 10000;
request.ReadWriteTimeout = 10000;
request.AddRange(0, fileSize - 1);
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
Stream responseStream = response.GetResponseStream();
byte[] buffer = new byte[1024];
FileStream fs = new FileStream(filePath, FileMode.Create);
int length = 0;
while ((length = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, length);
}
fs.Close();
responseStream.Close();
}

public static long GetFileSize(string url)
{
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "HEAD";
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
return response.ContentLength;
}
```

方式二:使用Nuget包实现断点续传下载

使用Nuget包“BrokenEvent.Shared”可以快速实现断点续传下载。示例代码如下:
csharp
var downloader = new HttpDownloader(new Uri(url), filePath);
downloader.BytesPerSecondSpeedLimit = 50000;
downloader.ProgressChanged += (object sender, HttpDownloadProgressChangedEventArgs e) =>
{
Console.Write($"\r{e.ProgressPercentage}% ({e.BytesDownloaded}/{e.TotalBytes})");
};
downloader.Start();

文件上传断点续传实现

在文件上传断点续传实现中,需要将文件分块,分别上传到服务端,服务端将文件分块拼接起来形成完整文件。具体实现步骤如下:
1. 根据文件大小和分块大小计算出需要分多少块

    long fileSize = new FileInfo(filePath).Length;
    int blockCount = (int) Math.Ceiling((double) fileSize / blockSize);
  1. 根据分块数量生成临时文件名,将各个分块分别保存在本地
    string tempPath = Path.GetDirectoryName(filePath);
    for (int i = 0; i < blockCount; i++)
    {
        string blockFileName = Path.Combine(tempPath, $"{fileName}.part{i}");
        long length = blockSize;
        if (i == blockCount - 1)
        {
            length = fileSize - i * blockSize;
        }
        byte[] buffer = new byte[length];
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            fs.Seek(i * blockSize, SeekOrigin.Begin);
            fs.Read(buffer, 0, (int)length);
        }
        using (FileStream fs = new FileStream(blockFileName, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            fs.Write(buffer, 0, buffer.Length);
        }
    }
  1. 分块上传,每个分块上传时需要带上当前分块的起始位置和长度,服务端需要根据这两个参数将分块拼接起来。如果上传过程中发生异常,可以记录当前上传到哪个分块,下次上传时直接从记录的位置继续上传即可。
    try
    {
        for (int i = 0; i < blockCount; i++)
        {
            string blockFileName = Path.Combine(tempPath, $"{fileName}.part{i}");
            FileInfo file = new FileInfo(blockFileName);
            long startPosition = i * blockSize;
            int length = (int)file.Length;
            byte[] buffer = new byte[length];
            using (FileStream fs = new FileStream(blockFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                fs.Read(buffer, 0, buffer.Length);
            }

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"{url}?fileName={fileName}&startIndex={startPosition}");
            request.Timeout = timeout;
            request.Method = "POST";

            Stream requestStream = request.GetRequestStream();
            requestStream.Write(buffer, 0, buffer.Length);
            requestStream.Close();

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            response.Close();

            finishedBlockNumber = i;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"上传出错:{ex.Message}");
    }
  1. 服务端接收分块时需要根据上传的起始位置挨个拼接各个分块,最终形成完整文件
    public void ReceiveFile(string fileName, long startIndex ,Stream fileStream)
    {
        string tempFilePath = Path.Combine(HttpContext.Current.Server.MapPath("~/Upload/Temp"), $"{fileName}.part{startIndex / blockSize}");
        if (!File.Exists(tempFilePath))
        {
            return;
        }
        FileStream fs = new FileStream(tempFilePath, FileMode.Append, FileAccess.Write, FileShare.Write);
        fileStream.CopyTo(fs);
        fs.Close();
        UploadStatus status = new UploadStatus()
        {
            FileName = fileName,
            StartIndex = startIndex,
            EndIndex = startIndex + fileStream.Length
        };
        UpdateUploadStatus(status);
        if (startIndex % blockSize == 0 && status.EndIndex == fileSize)
        {
            CombineBlocks(fileName);
        }
    }

    private void CombineBlocks(string fileName)
    {
        List<string> blockFiles = Directory.GetFiles(Path.Combine(HttpContext.Current.Server.MapPath("~/Upload/Temp"))).ToList();
        blockFiles = blockFiles.Where(f => Path.GetFileName(f).StartsWith($"{fileName}.part")).ToList();
        blockFiles.Sort(new FileNameComparer());
        using (FileStream fs = new FileStream(Path.Combine(savePath, fileName), FileMode.Append, FileAccess.Write, FileShare.Write))
        {
            foreach (string blockFile in blockFiles)
            {
                byte[] buffer = File.ReadAllBytes(blockFile);
                fs.Write(buffer, 0, buffer.Length);
                File.Delete(blockFile);
            }
            fs.Close();
        }
    }

总结

以上是C#文件断点续传实现的攻略,其中包括了文件下载和文件上传两个方面的实现,希望对需要实现文件断点续传功能的开发者有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#文件断点续传实现方法 - Python技术站

(0)
上一篇 2023年6月1日
下一篇 2023年6月1日

相关文章

  • C# DateTime.AddSeconds()方法: 将指定的秒数加到指定的日期上

    DateTime.AddSeconds()方法简介 在 C# 中,DateTime 类型的实例代表了日期和时间值。AddSeconds() 是 DateTime 类型提供的一个方法,它的作用是向 DateTime 对象添加指定的秒数。它会返回一个新的 DateTime 对象,表示当前对象加上指定秒数后的日期和时间。 方法定义如下: public DateTi…

    C# 2023年4月19日
    00
  • C#自定义异常就这么简单

    C#是一种强类型语言,可以捕获和处理各种异常,从而帮助我们发现程序中出现的错误。在程序开发过程中,如果需要找到特定的错误情况并处理,这时就需要创建自定义异常。本文将介绍如何在C#中创建和使用自定义异常。 1、什么是异常? 异常是指在程序执行期间发生的错误或异常情况,例如除法中除以0、文件不存在、内存不足等。当发生异常时,程序会停止执行当前的操作,并抛出一个异…

    C# 2023年5月9日
    00
  • C#实现几十万级数据导出Excel及Excel各种操作实例

    C#实现几十万级数据导出Excel及Excel各种操作实例 在C#中,我们可以使用第三方库EPPlus来处理Excel文件。以下是几步实现几十万级数据导出Excel的完整攻略: 步骤一:安装EPPlus库 我们可以在NuGet中添加EPPlus库,或是通过官方网站下载最新版。 步骤二:创建Excel文件 我们可以通过以下代码来创建一个Excel文件及相关信息…

    C# 2023年6月7日
    00
  • C# 使用HttpClient上传文件并附带其他参数的步骤

    针对这个问题,我将按照以下结构来详细讲解如何使用C#的HttpClient上传文件并附带其他参数: 上传文件的基本步骤 附带其他参数的上传步骤 示例1:上传文件并附带一个简单参数 示例2:上传多个文件并附带多个参数 1. 上传文件的基本步骤 要使用HttpClient上传文件,需要进行以下步骤: 创建一个实例的HttpClient类 构建一个实例的Multi…

    C# 2023年6月1日
    00
  • AspectCore和MSDI 实现Name注册以及解析对象

    AspectCore 在注册服务这块比较简单,默认是无法根据Name去注册和解析对象,这边做一下这块的扩展 大致原理是根据自定义Name去生成对应的动态类型,然后使用委托或者对象的方式,进行注册 tips:由于底层原理的原因,无法支持Type的方式进行注册   定义好动态类型的接口,以及相关实现 1 public interface INamedServic…

    C# 2023年4月27日
    00
  • Winform学生信息管理系统登陆窗体设计(1)

    下面是Winform学生信息管理系统登陆窗体设计的完整攻略。 一、设计思路 设计一个Winform学生信息管理系统登陆窗体需要以下几步: 新建一个Winform项目,添加登陆窗体。 设计登陆窗体的布局,包括添加组件,设置组件属性等。 为登陆窗体的组件添加事件处理程序,比如点击按钮触发登陆操作。 在事件处理程序中,编写验证登陆信息的代码,并执行相应的操作。 二…

    C# 2023年6月3日
    00
  • C# System.TypeInitializationException 异常处理方案

    首先我们来简单地了解一下什么是”System.TypeInitializationException”异常。 “System.TypeInitializationException”是.NET框架中的一种异常,它通常发生在类或结构体初始化时,当初始化过程中发生错误时就会抛出该类异常。例如,在类的静态构造函数中,初始化对象时出现错误,或者在静态变量初始化期间出…

    C# 2023年5月15日
    00
  • 如何使用C#程序给PDF文件添加编辑域

    下面是使用C#程序给PDF文件添加编辑域的完整攻略: 准备工作 在开始添加编辑域之前,我们需要准备一些工作。首先,我们需要下载和安装iTextSharp,这是一个开源的PDF编辑库。其次,我们需要安装Adobe Acrobat DC,这是一个非常流行的PDF编辑器,我们后续需要用它来验证PDF文件中添加的编辑域是否有效。 添加编辑域 一旦我们准备好了工作,我…

    C# 2023年6月1日
    00
合作推广
合作推广
分享本页
返回顶部