XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension

【DLL】XFE各类拓展是一个C#的DLL库,旨在优化C#代码中常用语句的使用,并提供更简洁的访问方式,同时提供Xunit测试框架,快速搭建服务器/客户端,免费ChatGPTAPI接口,免费通讯服务器,XFE下载器,新增格式等

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEExtension

XFE下载器更新部分内容

ed8f914
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

4 个文件 +216 -26
Modified XFE各类拓展.NetCore/FileExtension/FileExtension.cs +34 -0
@@ -227,4 +227,38 @@ public static class FileExtension
227 227 catch { }
228 228 return null; // 如果没有找到占用文件的进程,返回null
229 229 }
230 /// <summary>
231 /// 获取文件的有效起始位置
232 /// </summary>
233 /// <param name="fileStream">文件流</param>
234 /// <param name="startPosition">起始搜寻位置</param>
235 /// <param name="endPosition">结束搜寻位置</param>
236 /// <returns>一个长整型,介于起始位置和结束位置之间</returns>
237 public static async Task<long> GetValidPosition(this FileStream fileStream, long startPosition, long endPosition)
238 {
239 long seekPosition = endPosition;
240 fileStream.Seek(startPosition, SeekOrigin.Begin);
241 byte[] buffer = new byte[endPosition - startPosition];
242 await fileStream.ReadAsync(buffer.AsMemory(0, (int)(endPosition - startPosition)));
243 for (int i = buffer.Length - 1; i >= 0; i--)
244 {
245 if (buffer[i] != 0)
246 break;
247 seekPosition--;
248 }
249 return seekPosition > startPosition ? seekPosition : startPosition;
250 }
251 /// <summary>
252 /// 获取文件的有效起始位置
253 /// </summary>
254 /// <param name="fileStream">文件流</param>
255 /// <param name="startPosition">起始搜寻位置</param>
256 /// <returns>一个长整型,介于起始位置和结束位置之间</returns>
257 public static async Task<long> GetValidPosition(this FileStream fileStream, long startPosition) => await GetValidPosition(fileStream, startPosition, fileStream.Length);
258 /// <summary>
259 /// 获取文件的有效起始位置
260 /// </summary>
261 /// <param name="fileStream">文件流</param>
262 /// <returns>一个长整型,介于起始位置和结束位置之间</returns>
263 public static async Task<long> GetValidPosition(this FileStream fileStream) => await GetValidPosition(fileStream, 0, fileStream.Length);
230 264 }
Modified XFE各类拓展.NetCore/WebExtension/FileDownloadedEventArgs.cs +14 -2
@@ -3,7 +3,7 @@
3 3 /// <summary>
4 4 /// 字符下载事件
5 5 /// </summary>
6 public class FileDownloadedEventArgs(byte[] currentBuffer, long downloadedBufferSize, long? totalBufferSize, bool downloaded) : EventArgs
6 public class FileDownloadedEventArgs(byte[] currentBuffer, long downloadedBufferSize, long? totalBufferSize, long currentSegmentTotalBufferSize, long currentSegmentDownloadedBufferSize, int currentSegmentIndex, bool downloaded) : EventArgs
7 7 {
8 8
9 9 /// <summary>
@@ -11,7 +11,7 @@ public class FileDownloadedEventArgs(byte[] currentBuffer, long downloadedBuffer
11 11 /// </summary>
12 12 public byte[] CurrentBuffer { get; } = currentBuffer;
13 13 /// <summary>
14 /// 总计下载的字节长度
14 /// 总共已下载的字节大小
15 15 /// </summary>
16 16 public long DownloadedBufferSize { get; } = downloadedBufferSize;
17 17 /// <summary>
@@ -19,6 +19,18 @@ public class FileDownloadedEventArgs(byte[] currentBuffer, long downloadedBuffer
19 19 /// </summary>
20 20 public long? TotalBufferSize { get; } = totalBufferSize;
21 21 /// <summary>
22 /// 当前节的总计需要下载的字节大小
23 /// </summary>
24 public long CurrentSegmentTotalBufferSize { get; } = currentSegmentTotalBufferSize;
25 /// <summary>
26 /// 当前节的已下载的字节大小
27 /// </summary>
28 public long CurrentSegmentDownloadedBufferSize { get; } = currentSegmentDownloadedBufferSize;
29 /// <summary>
30 /// 当前节的索引
31 /// </summary>
32 public int CurrentSegmentIndex { get; } = currentSegmentIndex;
33 /// <summary>
22 34 /// 是否下载完成
23 35 /// </summary>
24 36 public bool Downloaded { get; } = downloaded;
Modified XFE各类拓展.NetCore/WebExtension/WebExtension.cs +16 -1
@@ -1,4 +1,5 @@
1 using System.Text;
1 using System.Net.Http;
2 using System.Text;
2 3 using XFE各类拓展.NetCore.TaskExtension;
3 4
4 5 namespace XFE各类拓展.NetCore.WebExtension;
@@ -152,4 +153,18 @@ public static class WebExtension
152 153 return null;
153 154 }
154 155 }
156 /// <summary>
157 /// 通过给定的URL获取其文件名
158 /// </summary>
159 /// <param name="url">指定的URL</param>
160 /// <returns></returns>
161 public static async Task<string?> GetFileNameFromURL(this string url)
162 {
163 var response = await new HttpClient().GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
164 response.EnsureSuccessStatusCode();
165 if (response.Content.Headers.ContentDisposition?.FileName is null)
166 return Path.GetFileName(response.RequestMessage?.RequestUri?.AbsolutePath);
167 else
168 return response.Content.Headers.ContentDisposition.FileNameStar;
169 }
155 170 }
Modified XFE各类拓展.NetCore/WebExtension/XFEDownloader.cs +152 -23
@@ -1,12 +1,20 @@
1 using XFE各类拓展.NetCore.DelegateExtension;
1 using System.Diagnostics.CodeAnalysis;
2 using XFE各类拓展.NetCore.DelegateExtension;
3 using XFE各类拓展.NetCore.FileExtension;
4 using XFE各类拓展.NetCore.TaskExtension;
2 5
3 6 namespace XFE各类拓展.NetCore.WebExtension;
4 7
5 8 /// <summary>
6 9 /// XFE下载器
7 10 /// </summary>
8 public class XFEDownloader
11 public class XFEDownloader : IDisposable
9 12 {
13 private bool disposedValue;
14 private readonly HttpClient httpClient = new();
15 private HttpResponseMessage? responseMessage;
16 private readonly List<HttpResponseMessage> httpResponseMessages = [];
17 private readonly List<Task> downloadTasks = [];
10 18 /// <summary>
11 19 /// 字节下载事件
12 20 /// </summary>
@@ -14,45 +22,166 @@ public class XFEDownloader
14 22 /// <summary>
15 23 /// 目标下载地址
16 24 /// </summary>
17 public required string DownloadUrl { get; set; }
25 public required string DownloadUrl { get; init; }
18 26 /// <summary>
19 27 /// 储存位置
20 28 /// </summary>
21 public required string SavePath { get; set; }
29 public required string SavePath { get; init; }
30 /// <summary>
31 /// 文件分段下载的数量(可加速下载)
32 /// </summary>
33 public int FileSegmentCount { get; init; } = 1;
22 34 /// <summary>
23 35 /// 已下载
24 36 /// </summary>
25 public bool Downloaded { get; }
37 public bool Downloaded { get; private set; }
38 /// <summary>
39 /// 暂停
40 /// </summary>
41 public bool IsPaused { get; set; }
26 42 /// <summary>
27 43 /// 开始下载
28 44 /// </summary>
29 /// <param name="continueFromLastDownload"></param>
45 /// <param name="continueFromLastDownload">是否为继续上次的下载</param>
30 46 /// <returns></returns>
31 47 public async Task Download(bool continueFromLastDownload = true)
32 48 {
33 using var client = new HttpClient();
34 49 var continueDownload = continueFromLastDownload && File.Exists(SavePath);
35 using var fileStream = new FileStream(SavePath, continueDownload ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
36 50 long totalRead = 0;
37 long lastBufferDownloadSize = fileStream.Length;
38 if (continueDownload)
51 responseMessage ??= await GetHttpResponseMessage();
52 long? totalFileSize = responseMessage.Content.Headers.ContentLength;
53 using var createFileStream = new FileStream(SavePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, 8192, true);
54 if (totalFileSize is not null)
55 createFileStream.SetLength(totalFileSize.Value);
56 for (int i = 0; i < FileSegmentCount; i++)
39 57 {
40 client.DefaultRequestHeaders.Range = new System.Net.Http.Headers.RangeHeaderValue(lastBufferDownloadSize, null);
41 totalRead = lastBufferDownloadSize;
58 int currentSegment = i;
59 downloadTasks.Add(Task.Run(async () =>
60 {
61 using var fileStream = new FileStream(SavePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 8192, true);
62 long lastBufferDownloadSize = continueDownload ? await fileStream.GetValidPosition() : fileStream.Length / FileSegmentCount * currentSegment;
63 long endPosition = currentSegment == FileSegmentCount - 1 ? fileStream.Length - fileStream.Length / FileSegmentCount * currentSegment : fileStream.Length / FileSegmentCount * (currentSegment + 1);
64 fileStream.Seek(lastBufferDownloadSize, SeekOrigin.Begin);
65 if (continueDownload)
66 {
67 httpClient.DefaultRequestHeaders.Range = new System.Net.Http.Headers.RangeHeaderValue(lastBufferDownloadSize, endPosition);
68 totalRead += lastBufferDownloadSize;
69 }
70 using var contentStream = await (await GetHttpResponseMessage()).Content.ReadAsStreamAsync();
71 byte[] buffer = new byte[8192];
72 int currentRead;
73 while ((currentRead = await contentStream.ReadAsync(buffer)) > 0 && !disposedValue)
74 {
75 if (IsPaused)
76 while (!IsPaused && !disposedValue) { }
77 await fileStream.WriteAsync(buffer.AsMemory(0, currentRead));
78 totalRead += currentRead;
79 var bufferCopy = new byte[8192];
80 bufferCopy.CopyTo(buffer, 0);
81 if (totalRead == totalFileSize)
82 Downloaded = true;
83 BufferDownloaded?.Invoke(this, new FileDownloadedEventArgs(bufferCopy, totalRead, totalFileSize, Downloaded));
84 }
85 }));
42 86 }
43 using var response = await client.GetAsync(DownloadUrl, HttpCompletionOption.ResponseHeadersRead);
87 await Task.WhenAll(downloadTasks);
88 }
89 /// <summary>
90 /// 暂停下载
91 /// </summary>
92 public void Pause() => IsPaused = true;
93 /// <summary>
94 /// 继续下载
95 /// </summary>
96 /// <returns></returns>
97 public async Task Continue() => await Download();
98 /// <summary>
99 /// 获取下载信息
100 /// </summary>
101 /// <returns></returns>
102 public async Task<(string? fileName, long? fileSize)> GetDownloadInfo()
103 {
104 responseMessage ??= await GetHttpResponseMessage();
105 return (Path.GetFileName(responseMessage.RequestMessage?.RequestUri?.AbsolutePath), responseMessage.Content.Headers.ContentLength);
106 }
107 private async Task<HttpResponseMessage> GetHttpResponseMessage()
108 {
109 var response = await httpClient.GetAsync(DownloadUrl, HttpCompletionOption.ResponseHeadersRead);
44 110 response.EnsureSuccessStatusCode();
45 long? totalFileSize = response.Content.Headers.ContentLength + lastBufferDownloadSize;
46 using var contentStream = await response.Content.ReadAsStreamAsync();
47 byte[] buffer = new byte[8192];
48 int currentRead;
49 while ((currentRead = await contentStream.ReadAsync(buffer)) > 0)
111 return response;
112 }
113 /// <summary>
114 /// 释放资源
115 /// </summary>
116 /// <param name="disposing"></param>
117 protected virtual void Dispose(bool disposing)
118 {
119 if (!disposedValue)
50 120 {
51 await fileStream.WriteAsync(buffer.AsMemory(0, currentRead));
52 totalRead += currentRead;
53 var bufferCopy = new byte[8192];
54 bufferCopy.CopyTo(buffer, 0);
55 BufferDownloaded?.Invoke(this, new FileDownloadedEventArgs(bufferCopy, totalRead, totalFileSize, currentRead != 8192));
121 if (disposing)
122 {
123 httpClient.Dispose();
124 responseMessage?.Dispose();
125 foreach (var response in httpResponseMessages)
126 {
127 response.Dispose();
128 }
129 foreach (var task in downloadTasks)
130 {
131 task.Dispose();
132 }
133 }
134
135 // TODO: 释放未托管的资源(未托管的对象)并重写终结器
136 // TODO: 将大型字段设置为 null
137 disposedValue = true;
56 138 }
57 139 }
140 /// <summary>
141 /// 释放资源
142 /// </summary>
143 ~XFEDownloader()
144 {
145 Dispose(disposing: false);
146 }
147 /// <summary>
148 /// 释放资源
149 /// </summary>
150 public void Dispose()
151 {
152 Dispose(disposing: true);
153 GC.SuppressFinalize(this);
154 }
155 /// <summary>
156 /// XFE下载器
157 /// </summary>
158 public XFEDownloader() { }
159 /// <summary>
160 /// XFE下载器
161 /// </summary>
162 /// <param name="url">下载的URL地址</param>
163 [SetsRequiredMembers]
164 public XFEDownloader(string url)
165 {
166 if (string.IsNullOrEmpty(url))
167 throw new ArgumentException($"“{nameof(url)}”不能为 null 或空。", nameof(url));
168 string filePath = url.GetFileNameFromURL().WaitAndGetResult()!;
169 DownloadUrl = url;
170 SavePath = filePath;
171 }
172 /// <summary>
173 /// XFE下载器
174 /// </summary>
175 /// <param name="url">下载的URL地址</param>
176 /// <param name="filePath">本地文件路径</param>
177 [SetsRequiredMembers]
178 public XFEDownloader(string url, string filePath)
179 {
180 if (string.IsNullOrEmpty(url))
181 throw new ArgumentException($"“{nameof(url)}”不能为 null 或空。", nameof(url));
182 if (string.IsNullOrEmpty(filePath))
183 throw new ArgumentException($"“{nameof(filePath)}”不能为 null 或空。", nameof(filePath));
184 DownloadUrl = url;
185 SavePath = filePath;
186 }
58 187 }