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);
- 根据分块数量生成临时文件名,将各个分块分别保存在本地
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);
}
}
- 分块上传,每个分块上传时需要带上当前分块的起始位置和长度,服务端需要根据这两个参数将分块拼接起来。如果上传过程中发生异常,可以记录当前上传到哪个分块,下次上传时直接从记录的位置继续上传即可。
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}");
}
- 服务端接收分块时需要根据上传的起始位置挨个拼接各个分块,最终形成完整文件
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技术站