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#中 Thread,Task,Async/Await,IAsyncResult的那些事儿

    详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿 多线程编程是现代软件开发中非常重要的一个方向。在C#中,有多种方式来进行多线程编程,其中 Thread,Task,Async/Await,IAsyncResult 是最常用的几种方式。 Thread Thread 表示线程类。它允许我们在应用程序中创建新线程来执行…

    C# 2023年6月6日
    00
  • Python集成C#实现界面操作下载文件功能的全过程

    下面我将详细讲解如何使用Python集成C#程序实现界面操作下载文件的全过程。 准备工作 在开始之前我们需要做一些准备工作: 安装 Python 和 .NET Framework (C#程序需要运行在.NET环境下) 安装 Python 通过.NET控制C#程序的模块 pythonnet 编写C#程序,实现下载文件的功能 编写 Python 程序界面,通过调…

    C# 2023年5月15日
    00
  • C# BinaryWriter.Close – 关闭二进制编写器

    BinaryWriter.Close 方法是 C# 中 FileStream 的辅助写入器,用于在写入完毕后关闭流并释放资源。本文将详细讲解 BinaryWriter.Close 方法的作用及用法。 方法作用 BinaryWriter.Close 方法的作用是关闭该写入器所关联的 FileStream 并释放资源,避免流的泄漏。 方法语法 BinaryWri…

    C# 2023年4月19日
    00
  • asp.net(C#)使用QRCode生成图片中心加Logo或图像的二维码实例

    这里是”asp.net(C#)使用QRCode生成图片中心加Logo或图像的二维码实例”的完整攻略。 1. 背景和需求 在很多场景中,我们需要使用二维码来进行信息传递和交流。而一般的二维码相对来说显得过于平淡无奇,很多时候却又不能脱离二维码的原本用途。那么这时,我们就可以使用带有Logo或图像的二维码来达到更好的显示效果,增加视觉冲击力,吸引更多的用户获取信…

    C# 2023年6月1日
    00
  • C#绘制实时曲线图的方法详解

    针对网站上的这篇文章“C#绘制实时曲线图的方法详解”,以下是完整的攻略: 1. 了解实时曲线图的作用和实现原理 实时曲线图主要是用于显示一些随时间变化的数据,并实时更新数据,常见的使用场景是嵌入式监控、数据采集和控制等。关于实时曲线图的实现原理,一般使用的是C#中的Graphics技术。 2. 准备工作 引入namespace: using System.D…

    C# 2023年6月7日
    00
  • C#判断字符串中是否包含指定字符串及contains与indexof方法效率问题

    C#中判断一个字符串是否包含子字符串是一个常用的任务。本文将讲解如何使用C#的contains和indexof方法来实现这个任务,并探讨它们的效率问题。 contains方法 contains方法是String类中的一种方法,用于判断一个字符串是否包含指定的子字符串。代码示例如下: string str1 = "hello world";…

    C# 2023年6月8日
    00
  • C#中增强类功能的几种方式详解

    C#中增强类功能的几种方式详解 1.继承 继承是C#中一种重要的增强类功能方式。子类可以继承父类的属性和方法,从而实现代码的复用和扩展。 继承的实现方式是使用冒号连接子类和父类,例如: public class ParentClass { public void ParentMethod() { Console.WriteLine("This is…

    C# 2023年6月1日
    00
  • c#中oracle的to_date函数使用方法

    讲解C#中Oracle的to_date函数使用方法需要以下过程: 第一步:了解to_date函数 在Oracle中,to_date函数是用来将字符串转化为日期类型的函数。它的常用语法如下: to_date(‘日期字符串’, ‘日期格式化字符串’) 其中,日期字符串是要转化的字符串,日期格式化字符串则表示日期字符串的表现形式,例如’yyyy-mm-dd’。 在…

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