├── Logo.png
├── .gitignore
├── examples
├── download
│ ├── download.csproj
│ └── Program.cs
├── example1
│ ├── example1.csproj
│ └── Program.cs
└── TDown
│ ├── TDown.csproj
│ ├── Program.cs
│ ├── Form1.cs
│ └── Form1.Designer.cs
├── src
├── HttpDown
│ ├── HttpDownExtension.cs
│ ├── HttpDown.Option.cs
│ └── HttpDown.cs
├── Core
│ ├── ITask.cs
│ ├── Val.cs
│ ├── Files.cs
│ ├── HttpOption.cs
│ ├── Api.cs
│ ├── Http.cs
│ └── MimeMapping.cs
├── HttpLib.csproj
├── HttpCore.Call.cs
├── Config.cs
├── HttpCore.cs
└── HttpCore.Request.cs
├── LICENSE
├── HttpLib.sln
└── README.md
/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eviav/HttpLib/HEAD/Logo.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build and Release Folders
2 | bin-debug/
3 | bin-release/
4 | [Oo]bj/
5 | [Bb]in/
6 |
7 | # Other files and folders
8 | *.csproj.user
9 |
10 | # Executables
11 | *.swf
12 | *.air
13 | *.ipa
14 | *.apk
15 | *.vcxproj.filters
16 | *.vcxproj.user
17 | *.pubxml.user
18 |
19 | /.vs
20 | /packages
21 | /nuget*
22 |
--------------------------------------------------------------------------------
/examples/download/download.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/example1/example1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/HttpDown/HttpDownExtension.cs:
--------------------------------------------------------------------------------
1 | namespace HttpLib
2 | {
3 | public static class HttpDownExtension
4 | {
5 | ///
6 | /// 下载(多线程)
7 | ///
8 | /// 核心
9 | /// 保存路径
10 | /// 任务id
11 | public static HttpDown downLoad(this HttpCore core, string savePath, string? id = null) => new HttpDown(core, savePath, id);
12 | }
13 | }
--------------------------------------------------------------------------------
/examples/TDown/TDown.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net6.0-windows
6 | true
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Core/ITask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace HttpLib
5 | {
6 | public class ITask
7 | {
8 | public static Task Run(Action action, Action? end = null)
9 | {
10 | if (end == null)
11 | {
12 | #if NET40
13 | return Task.Factory.StartNew(action);
14 | #else
15 | return Task.Run(action);
16 | #endif
17 | }
18 | #if NET40
19 | return Task.Factory.StartNew(action).ContinueWith(action => { end(); });
20 | #else
21 | return Task.Run(action).ContinueWith(action => { end(); });
22 | #endif
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/examples/TDown/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Windows.Forms;
4 |
5 | namespace TDown
6 | {
7 | static class Program
8 | {
9 | public static System.Diagnostics.Process Pro = System.Diagnostics.Process.GetCurrentProcess();
10 | public static string ExePath = Pro.MainModule.FileName;
11 | public static string BasePath = new FileInfo(ExePath).DirectoryName + Path.DirectorySeparatorChar;
12 |
13 | ///
14 | /// The main entry point for the application.
15 | ///
16 | [STAThread]
17 | static void Main()
18 | {
19 | Application.SetHighDpiMode(HighDpiMode.SystemAware);
20 | Application.EnableVisualStyles();
21 | Application.SetCompatibleTextRenderingDefault(false);
22 | Application.Run(new Form1());
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Administrators
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Core/Val.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace HttpLib
4 | {
5 | public class Val
6 | {
7 | public Val(string key, int? value) : this(key, value?.ToString()) { }
8 | public Val(string key, long? value) : this(key, value?.ToString()) { }
9 | public Val(string key, double? value) : this(key, value?.ToString()) { }
10 | public Val(string key, int value) : this(key, value.ToString()) { }
11 | public Val(string key, long value) : this(key, value.ToString()) { }
12 | public Val(string key, double value) : this(key, value.ToString()) { }
13 | public Val(string key, string? value)
14 | {
15 | Key = key;
16 | Value = value;
17 | }
18 |
19 | ///
20 | /// 键
21 | ///
22 | public string Key { get; set; }
23 | ///
24 | /// 值
25 | ///
26 | public string? Value { get; set; }
27 |
28 | public override string ToString() => Key + "=" + Value;
29 |
30 | public string ToStringEscape()
31 | {
32 | if (Value != null) return Key + "=" + Uri.EscapeDataString(Value);
33 | else return Key + "=";
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/HttpLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net4.0;net4.5;net4.6;net4.8;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0
4 | Tom
5 | 10.0
6 | disable
7 | enable
8 | $(Version)
9 | $(Version)
10 | 3.0.6
11 | Copyright © Tom 2021-2025
12 | 便捷的HttpClient库
13 | 支持GET/POST/PUT/Delete 上传文件/文件参数
14 | ===========================
15 | 使用HttpWebRequest实现
16 | Tom.HttpLib
17 | Logo.png
18 | https://github.com/Haku-Men/HttpLib
19 | git
20 | Http;HttpLib;HttpHelper;HttpClient;HttpDown;DownLoad
21 | https://github.com/EVA-SS/HttpLib
22 | README.md
23 | True
24 | False
25 | MIT
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/examples/download/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 |
3 | using HttpLib;
4 |
5 | var savapath = Http.Get("https://dldir1.qq.com/qqfile/qq/QQNT/Windows/QQ_9.9.9_240422_x64_01.exe")
6 | .redirect()
7 | .responseProgres((bytesSent, totalBytes) =>
8 | {
9 | Console.SetCursorPosition(0, 0);
10 | if (totalBytes > 0)
11 | {
12 | double prog = (bytesSent * 1.0) / (totalBytes * 1.0);
13 | Console.Write("{0}% 下载 {1}/{2} ", Math.Round(prog * 100.0, 1).ToString("N1"), CountSize(bytesSent), CountSize(totalBytes));
14 | }
15 | else
16 | {
17 | Console.Write("{0} 下载 ", CountSize(bytesSent));
18 | }
19 | }).download(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "qq.exe");
20 |
21 | if (savapath != null) Console.WriteLine("下载成功保存至:" + savapath);
22 | else Console.WriteLine("下载失败");
23 |
24 |
25 | Console.ReadLine();
26 |
27 | static string CountSize(double Size)
28 | {
29 | string houzui = "B";
30 | double FactSize = Size;
31 | if (FactSize >= 1024)
32 | {
33 | houzui = "K";
34 | FactSize /= 1024.00;
35 | }
36 | if (FactSize >= 1024)
37 | {
38 | houzui = "M";
39 | FactSize /= 1024.00;
40 | }
41 | if (FactSize >= 1024)
42 | {
43 | houzui = "G";
44 | FactSize /= 1024.00;
45 | }
46 | if (FactSize >= 1024)
47 | {
48 | houzui = "T";
49 | FactSize /= 1024.00;
50 | }
51 | return string.Format("{0} {1}", Math.Round(FactSize, 2), houzui);
52 | }
--------------------------------------------------------------------------------
/src/Core/Files.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace HttpLib
4 | {
5 | ///
6 | /// 文件
7 | ///
8 | public class Files
9 | {
10 | ///
11 | /// 参数名称
12 | ///
13 | public string Name { get; set; }
14 | ///
15 | /// 文件名
16 | ///
17 | public string FileName { get; set; }
18 | ///
19 | /// 文件类型
20 | ///
21 | public string ContentType { get; set; }
22 | ///
23 | /// 文件流
24 | ///
25 | public Stream Stream { get; set; }
26 |
27 | ///
28 | /// 文件大小
29 | ///
30 | public long Size { get; private set; }
31 |
32 | ///
33 | /// 添加文件
34 | ///
35 | /// 参数名称
36 | /// 文件名称
37 | /// 文件类型
38 | /// 字节流
39 | public Files(string name, string fileName, string contentType, byte[] data)
40 | {
41 | Name = name;
42 | FileName = fileName;
43 | ContentType = contentType;
44 | Size = data.Length;
45 | Stream = new MemoryStream(data);
46 | }
47 |
48 | ///
49 | /// 添加文件
50 | ///
51 | /// 文件名称
52 | /// 文件类型
53 | /// 字节流
54 | public Files(string fileName, string contentType, byte[] data) : this("file", fileName, contentType, data)
55 | { }
56 |
57 | ///
58 | /// 添加文件
59 | ///
60 | /// 参数名称
61 | /// 文件路径
62 | public Files(string name, string fullName)
63 | {
64 | Name = name;
65 | var fileInfo = new FileInfo(fullName);
66 | FileName = fileInfo.Name;
67 | ContentType = MimeMapping.GetMimeMapping(fullName);
68 | Stream = File.OpenRead(fullName);
69 | Size = Stream.Length;
70 | }
71 |
72 | ///
73 | /// 添加文件
74 | ///
75 | /// 文件路径
76 | public Files(string fullName) : this("file", fullName) { }
77 | }
78 | }
--------------------------------------------------------------------------------
/HttpLib.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32616.157
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{9F853A87-AAE2-4752-9019-E8D1C43FB10E}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "example1", "examples\example1\example1.csproj", "{ECE9ECDB-4E9B-4BAE-A0AE-826F095E7272}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpLib", "src\HttpLib.csproj", "{BB60B69E-6842-4421-8068-EB59B8899B9E}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "download", "examples\download\download.csproj", "{23FDA92E-C67E-401B-9E54-E44BF1E2220D}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TDown", "examples\TDown\TDown.csproj", "{EB71932D-C4E7-47B1-9FB9-36604FE648E2}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {ECE9ECDB-4E9B-4BAE-A0AE-826F095E7272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {ECE9ECDB-4E9B-4BAE-A0AE-826F095E7272}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {ECE9ECDB-4E9B-4BAE-A0AE-826F095E7272}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {ECE9ECDB-4E9B-4BAE-A0AE-826F095E7272}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {BB60B69E-6842-4421-8068-EB59B8899B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {BB60B69E-6842-4421-8068-EB59B8899B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {BB60B69E-6842-4421-8068-EB59B8899B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {BB60B69E-6842-4421-8068-EB59B8899B9E}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {23FDA92E-C67E-401B-9E54-E44BF1E2220D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {23FDA92E-C67E-401B-9E54-E44BF1E2220D}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {23FDA92E-C67E-401B-9E54-E44BF1E2220D}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {23FDA92E-C67E-401B-9E54-E44BF1E2220D}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {EB71932D-C4E7-47B1-9FB9-36604FE648E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {EB71932D-C4E7-47B1-9FB9-36604FE648E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {EB71932D-C4E7-47B1-9FB9-36604FE648E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {EB71932D-C4E7-47B1-9FB9-36604FE648E2}.Release|Any CPU.Build.0 = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | GlobalSection(NestedProjects) = preSolution
43 | {ECE9ECDB-4E9B-4BAE-A0AE-826F095E7272} = {9F853A87-AAE2-4752-9019-E8D1C43FB10E}
44 | {23FDA92E-C67E-401B-9E54-E44BF1E2220D} = {9F853A87-AAE2-4752-9019-E8D1C43FB10E}
45 | {EB71932D-C4E7-47B1-9FB9-36604FE648E2} = {9F853A87-AAE2-4752-9019-E8D1C43FB10E}
46 | EndGlobalSection
47 | GlobalSection(ExtensibilityGlobals) = postSolution
48 | SolutionGuid = {0B251537-182B-4B3E-9549-9AC4710A1C90}
49 | EndGlobalSection
50 | EndGlobal
51 |
--------------------------------------------------------------------------------
/src/HttpCore.Call.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 |
4 | namespace HttpLib
5 | {
6 | partial class HttpCore
7 | {
8 | #region 回调
9 |
10 | #region 上传进度的回调
11 |
12 | Action _requestProgres = null;
13 | Action _requestProgresMax = null;
14 | ///
15 | /// 上传进度的回调函数
16 | ///
17 | public HttpCore requestProgres(Action action)
18 | {
19 | _requestProgres = action;
20 | return this;
21 | }
22 | public HttpCore requestProgresMax(Action action)
23 | {
24 | _requestProgresMax = action;
25 | return this;
26 | }
27 |
28 | #endregion
29 |
30 | #region 下载进度的回调
31 |
32 | Action _responseProgres = null;
33 | Action _responseProgresMax = null;
34 | ///
35 | /// 下载进度的回调函数
36 | ///
37 | public HttpCore responseProgres(Action action)
38 | {
39 | _responseProgres = action;
40 | return this;
41 | }
42 |
43 | ///
44 | /// 下载进度的回调函数
45 | ///
46 | public HttpCore responseProgresMax(Action action)
47 | {
48 | _responseProgresMax = action;
49 | return this;
50 | }
51 |
52 | #endregion
53 |
54 | #endregion
55 |
56 | #region 请求
57 |
58 | Action action_Request = null;
59 | public HttpCore webrequest(Action action)
60 | {
61 | action_Request = action;
62 | return this;
63 | }
64 |
65 | Func action_before = null;
66 | ///
67 | /// 请求之前处理
68 | ///
69 | /// 请求之前处理回调
70 | /// 返回true继续 反之取消请求
71 | public HttpCore before(Func action)
72 | {
73 | action_before = action;
74 | return this;
75 | }
76 |
77 | Action action_fail = null;
78 | ///
79 | /// 接口调用失败的回调函数
80 | ///
81 | /// 错误Http响应头+错误
82 | ///
83 | public HttpCore fail(Action action)
84 | {
85 | action_fail = action;
86 | return this;
87 | }
88 |
89 | #endregion
90 |
91 | #region 终止
92 |
93 | public void abort()
94 | {
95 | if (req != null)
96 | {
97 | try
98 | {
99 | req.Abort();
100 | req = null;
101 | }
102 | catch
103 | { }
104 | }
105 | if (response != null)
106 | {
107 | try
108 | {
109 | response.Close();
110 | #if !NET40
111 | response.Dispose();
112 | #endif
113 | response = null;
114 | }
115 | catch
116 | { }
117 | }
118 | }
119 |
120 | #endregion
121 | }
122 | }
--------------------------------------------------------------------------------
/examples/example1/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 |
3 | using HttpLib;
4 |
5 | Config.fail += Config_fail;//全局异常
6 |
7 | List headerss = new List {
8 | new Val("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"),
9 | new Val("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36 Edg/86.0.622.63"),
10 | };
11 |
12 | var ip = Http.Get("https://www.baidu.com/s").IP;
13 | Console.WriteLine("IP:baidu.com " + ip);
14 | var a = Http.Get("https://www.baidu.com/s").header(headerss)
15 | .data(new { wd = "GitHub - Haku-Men HttpLib" })
16 | .data(new Val("ie", "utf-8"))
17 | //.header(new { userAgent = "测试1", accept = "*/*" })
18 | .redirect(true)
19 | .before((response, result) =>
20 | {
21 | return true; //继续请求
22 | })
23 | .fail(result =>
24 | {
25 | if (result.Exception == null) return;
26 | Console.Write(result.Exception.GetType());
27 | Console.WriteLine(result.Exception.Message);
28 | }).request(out _);
29 | Console.SetCursorPosition(0, 5);
30 | Console.WriteLine(a);
31 | Console.ReadLine();
32 |
33 | return;
34 |
35 | List headers = new List {
36 | new Val("accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"),
37 | new Val("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36 Edg/86.0.622.63"),
38 | };
39 | var a2 = Http.Post("http://localhost:61489/api/values").header(headers)
40 | .data(new { wd = new string[] { "GitHub - Haku-Men HttpLib", "321231" } })
41 | .data(new List { new Files(@"C:\Users\ttgx\Desktop\Flowing.exe") })
42 | .data(new List { new Files(@"C:\Users\ttgx\Desktop\ABT44B9D51AC6E96DA9220E39EED30D1945666375E8401AD7DE974193FD48211E72.jfif") })
43 | .data(new Val("abc", "123"))
44 | .data(new Val("abc1", "1234"))
45 | .requestProgres((bytesSent, totalBytes) =>
46 | {
47 | double prog = (bytesSent * 1.0) / (totalBytes * 1.0);
48 |
49 | int top = 0;
50 | Console.SetCursorPosition(0, top);
51 | Console.Write("{0}% 上传 {1}/{2} ", Math.Round(prog * 100.0, 1).ToString("N1"), CountSize(bytesSent), CountSize(totalBytes));
52 |
53 | })
54 | .responseProgres((bytesSent, totalBytes) =>
55 | {
56 | int top = 2;
57 | Console.SetCursorPosition(0, top);
58 | if (totalBytes > 0)
59 | {
60 | double prog = (bytesSent * 1.0) / (totalBytes * 1.0);
61 | Console.Write("{0}% 下载 {1}/{2} ", Math.Round(prog * 100.0, 1).ToString("N1"), CountSize(bytesSent), CountSize(totalBytes));
62 | }
63 | else
64 | {
65 | Console.Write("{0} 下载 ", CountSize(bytesSent));
66 | }
67 | }).fail(result =>
68 | {
69 | }).request(out _);
70 | Console.SetCursorPosition(0, 5);
71 | Console.WriteLine(a2);
72 | Console.ReadLine();
73 |
74 | static void Config_fail(HttpCore core, ResultResponse result)
75 | {
76 | if (result.Exception == null) return;
77 | Console.Write(result.Exception.GetType());
78 | Console.Write(result.Exception.Message);
79 | }
80 | static string CountSize(double Size)
81 | {
82 | string houzui = "B";
83 | double FactSize = Size;
84 | if (FactSize >= 1024)
85 | {
86 | houzui = "K";
87 | FactSize = (FactSize / 1024.00);
88 | }
89 | if (FactSize >= 1024)
90 | {
91 | houzui = "M";
92 | FactSize = (FactSize / 1024.00);
93 | }
94 | if (FactSize >= 1024)
95 | {
96 | houzui = "G";
97 | FactSize = (FactSize / 1024.00);
98 | }
99 | if (FactSize >= 1024)
100 | {
101 | houzui = "T";
102 | FactSize = (FactSize / 1024.00);
103 | }
104 | return string.Format("{0} {1}", Math.Round(FactSize, 2), houzui);
105 | }
--------------------------------------------------------------------------------
/src/Core/HttpOption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 | using System.Text;
5 |
6 | namespace HttpLib
7 | {
8 | public class HttpOption
9 | {
10 | public HttpOption(string url) { uri = new Uri(url); }
11 | public HttpOption(string url, HttpMethod _method)
12 | {
13 | uri = new Uri(url);
14 | method = _method;
15 | }
16 | public HttpOption(Uri _uri, HttpMethod _method)
17 | {
18 | uri = _uri;
19 | method = _method;
20 | }
21 |
22 | public Uri uri { get; }
23 | public HttpMethod method { get; set; } = HttpMethod.Get;
24 |
25 | #region 参数
26 |
27 | ///
28 | /// Get参数
29 | ///
30 | public List? query { get; set; }
31 | ///
32 | /// 参数
33 | ///
34 | public List? data { get; set; }
35 | ///
36 | /// body参数
37 | ///
38 | public string? datastr { get; set; }
39 |
40 | ///
41 | /// 上传文件
42 | ///
43 | public List? file { get; set; }
44 |
45 | #endregion
46 |
47 | #region 头
48 |
49 | ///
50 | /// 头
51 | ///
52 | public List? header { get; set; }
53 |
54 | #endregion
55 |
56 | ///
57 | /// 代理
58 | ///
59 | public IWebProxy? proxy { get; set; }
60 |
61 | ///
62 | /// 编码
63 | ///
64 | public Encoding? encoding { get; set; }
65 |
66 | ///
67 | /// 自动编码
68 | ///
69 | public bool autoencode { get; set; }
70 |
71 | ///
72 | /// 请求重定向
73 | ///
74 | public bool redirect { get; set; }
75 |
76 | ///
77 | /// 请求超时时长
78 | ///
79 | public int timeout { get; set; }
80 |
81 | ///
82 | /// 获取域名IP
83 | ///
84 | public string? IP
85 | {
86 | get
87 | {
88 | try
89 | {
90 | if (IPAddress.TryParse(uri.Host, out IPAddress? ip)) return ip.ToString();
91 | else
92 | {
93 | var hostEntry = Dns.GetHostEntry(uri.Host);
94 | var ipEndPoint = new IPEndPoint(hostEntry.AddressList[0], 0);
95 | string _ip = ipEndPoint.Address.ToString();
96 | if (_ip.StartsWith("::")) return "127.0.0.1";
97 | else return _ip;
98 | }
99 | }
100 | catch { }
101 | return null;
102 | }
103 | }
104 |
105 | ///
106 | /// 请求URL
107 | ///
108 | public Uri Url
109 | {
110 | get
111 | {
112 | #region 合并参数
113 |
114 | var data = new List();
115 | if (query != null && query.Count > 0) foreach (var it in query) data.Add(it.ToStringEscape());
116 |
117 | if (method == HttpMethod.Get && this.data != null && this.data.Count > 0) foreach (var it in this.data) data.Add(it.ToStringEscape());
118 |
119 | #endregion
120 |
121 | if (data.Count > 0)
122 | {
123 | if (string.IsNullOrEmpty(uri.Query)) return new Uri(uri.AbsoluteUri + "?" + string.Join("&", data));
124 | else return new Uri(uri.AbsoluteUri + "&" + string.Join("&", data));
125 | }
126 |
127 | return uri;
128 | }
129 | }
130 |
131 | public string FileName(ResultResponse _web)
132 | {
133 | if (_web.Header.ContainsKey("Content-Disposition"))
134 | {
135 | string val = _web.Header["Content-Disposition"];
136 | if (!string.IsNullOrEmpty(val)) return val.FileNameDisposition() ?? uri.FileName();
137 | }
138 | return uri.FileName();
139 | }
140 |
141 | public override string ToString() => uri.AbsoluteUri;
142 | }
143 | }
--------------------------------------------------------------------------------
/src/Config.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 |
5 | namespace HttpLib
6 | {
7 | ///
8 | /// 全局配置
9 | ///
10 | public class Config
11 | {
12 | public static string? CacheFolder = null;
13 |
14 | #region 请求头
15 |
16 | public static List? headers = null;
17 |
18 | ///
19 | /// 请求头
20 | ///
21 | /// 多个参数
22 | public static void header(params Val[] vals)
23 | {
24 | headers ??= new List(vals.Length);
25 | headers.AddRange(vals);
26 | }
27 |
28 | ///
29 | /// 请求头
30 | ///
31 | /// 多个参数
32 | public static void header(IList vals)
33 | {
34 | headers ??= new List(vals.Count);
35 | headers.AddRange(vals);
36 | }
37 |
38 | ///
39 | /// 请求头
40 | ///
41 | /// 键
42 | /// 值
43 | public static void header(string key, string? val)
44 | {
45 | if (headers == null) headers = new List { new Val(key, val) };
46 | else headers.Add(new Val(key, val));
47 | }
48 |
49 | ///
50 | /// 请求头
51 | ///
52 | /// 多个参数
53 | public static void header(IDictionary vals)
54 | {
55 | headers ??= new List(vals.Count);
56 | foreach (var it in vals) headers.Add(new Val(it.Key, it.Value));
57 | }
58 |
59 | #endregion
60 |
61 | ///
62 | /// 用户标识
63 | ///
64 | public static string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36";
65 |
66 | ///
67 | /// 表示文件压缩和解压缩编码格式,该格式将用来压缩在 System.Net.HttpWebRequest 的响应中收到的数据
68 | ///
69 | public static DecompressionMethods DecompressionMethod = DecompressionMethods.GZip;
70 |
71 | ///
72 | /// 全局是否自动重定向
73 | ///
74 | public static bool Redirect = false;
75 |
76 | #region 全局错误
77 |
78 | public delegate void ErrEventHandler(HttpCore core, ResultResponse result);
79 |
80 | ///
81 | /// 接口调用失败的回调函数(带响应头)
82 | ///
83 | public static event ErrEventHandler? fail;
84 |
85 | public static void OnFail(HttpCore core, ResultResponse result) => fail?.Invoke(core, result);
86 |
87 | #endregion
88 |
89 | #region 代理
90 |
91 | public static IWebProxy? _proxy = null;
92 |
93 | ///
94 | /// 全局代理
95 | ///
96 | /// 代理服务器的 URI
97 | public static void proxy(string address) => _proxy = new WebProxy(address);
98 | ///
99 | /// 全局代理
100 | ///
101 | /// 代理服务器的 URI
102 | public static void proxy(Uri address) => _proxy = new WebProxy(address);
103 |
104 | ///
105 | /// 全局代理
106 | ///
107 | /// 代理主机的名称
108 | /// 要使用的 Host 上的端口号
109 | public static void proxy(string host, int port) => _proxy = new WebProxy(host, port);
110 |
111 | ///
112 | /// 全局代理
113 | ///
114 | /// 代理主机的名称
115 | /// 要使用的 Host 上的端口号
116 | /// 用户名
117 | /// 密码
118 | public static void proxy(string host, int port, string username, string password)
119 | {
120 | _proxy = new WebProxy(host, port);
121 | if (!string.IsNullOrEmpty(username)) _proxy.Credentials = new NetworkCredential(username, password);
122 | }
123 |
124 | #endregion
125 |
126 | public static int CacheSize = 4096;
127 |
128 | ///
129 | /// 重试次数
130 | ///
131 | public static int RetryCount = 3;
132 | ///
133 | /// 超时时长
134 | ///
135 | public static int TimeOut = 10000;
136 | }
137 | }
--------------------------------------------------------------------------------
/src/Core/Api.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Net.Mime;
5 | using System.Threading;
6 |
7 | namespace HttpLib
8 | {
9 | internal static class Api
10 | {
11 | #region 文件处理
12 |
13 | public static bool DeleteFile(this string dir)
14 | {
15 | if (File.Exists(dir))
16 | {
17 | try
18 | {
19 | File.Delete(dir);
20 | }
21 | catch { return false; }
22 | }
23 | return true;
24 | }
25 |
26 | #region 删除文件夹
27 |
28 | ///
29 | /// 删除文件夹以及文件夹内所有文件
30 | ///
31 | public static bool DeleteDirectory(this string dir)
32 | {
33 | if (Directory.Exists(dir))
34 | {
35 | try
36 | {
37 | DeleteDirectory(new DirectoryInfo(dir));
38 | Directory.Delete(dir, true);
39 | }
40 | catch { return false; }
41 | }
42 | return true;
43 | }
44 |
45 | ///
46 | /// 清除文件夹内容,但是保留文件夹
47 | ///
48 | public static bool ClearDirectory(this string dir)
49 | {
50 | if (Directory.Exists(dir))
51 | {
52 | try
53 | {
54 | DeleteDirectory(new DirectoryInfo(dir));
55 | }
56 | catch { return false; }
57 | }
58 | else Directory.CreateDirectory(dir);
59 | return true;
60 | }
61 | public static void CreateDirectory(this string dir)
62 | {
63 | if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
64 | }
65 | static void DeleteDirectory(DirectoryInfo dir)
66 | {
67 | if (dir.Exists)
68 | {
69 | foreach (FileInfo item in dir.GetFiles())
70 | {
71 | try
72 | {
73 | item.Delete();
74 | }
75 | catch { }
76 | }
77 | foreach (DirectoryInfo item in dir.GetDirectories()) DeleteDirectory(item);
78 | }
79 | }
80 |
81 | #endregion
82 |
83 | #endregion
84 |
85 | public static string RandomString(this int length)
86 | {
87 | string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";
88 | char[] chars = new char[length];
89 | var rd = new Random();
90 | for (int i = 0; i < length; i++) chars[i] = allowedChars[rd.Next(0, allowedChars.Length)];
91 | return new string(chars);
92 | }
93 |
94 | public static string? FileNameDisposition(this string disposition)
95 | {
96 | if (disposition.Contains("filename*=UTF-8"))
97 | {
98 | try
99 | {
100 | return Uri.UnescapeDataString(disposition.Substring(disposition.IndexOf("filename*=UTF-8") + 15).Trim('\''));
101 | }
102 | catch { }
103 | }
104 | return new ContentDisposition(disposition).FileName;
105 | }
106 | public static string FileName(this Uri uri)
107 | {
108 | if (uri.Query.Length > 0) return Path.GetFileName(uri.AbsoluteUri.Substring(0, uri.AbsoluteUri.Length - uri.Query.Length));
109 | return Path.GetFileName(uri.AbsoluteUri);
110 | }
111 |
112 | public static string FileName(this Uri uri, string? disposition)
113 | {
114 | if (disposition != null && !string.IsNullOrEmpty(disposition)) return disposition.FileNameDisposition() ?? uri.FileName();
115 | return uri.FileName();
116 | }
117 |
118 | ///
119 | /// 文件合并函数,
120 | /// 可将任意个子文件合并为一个,为fileSplit()的逆过程
121 | /// delet标识是否删除原文件, change对data的首字节进行解密
122 | ///
123 | public static string CombineMultipleFilesIntoSingleFile(this List files, string filePath, string WorkPath)
124 | {
125 | if (files.Count == 1)
126 | {
127 | if (File.Exists(filePath)) File.Delete(filePath);
128 | File.Move(files[0], filePath);
129 | //删除临时文件夹
130 | WorkPath.DeleteDirectory();
131 | return filePath;
132 | }
133 | using (var MergeFile = new FileStream(filePath, FileMode.Create))
134 | {
135 | using (var AddWriter = new BinaryWriter(MergeFile))
136 | {
137 | //按序号排序
138 | int i = 0;
139 | foreach (string file in files)
140 | {
141 | i++;
142 | using (var fs = new FileStream(file, FileMode.Open))
143 | {
144 | using (var tmp = new BinaryReader(fs))
145 | {
146 | if (i == files.Count) AddWriter.Write(tmp.ReadBytes((int)fs.Length));
147 | else AddWriter.Write(tmp.ReadBytes((int)fs.Length - 1));
148 | }
149 | }
150 | }
151 | }
152 | //删除临时文件夹
153 | WorkPath.DeleteDirectory();
154 | }
155 | return filePath;
156 | }
157 |
158 | public static bool Wait(this WaitHandle handle)
159 | {
160 | try
161 | {
162 | handle.WaitOne();
163 | return false;
164 | }
165 | catch
166 | {
167 | return true;
168 | }
169 | }
170 | public static bool Wait(this CancellationTokenSource? token)
171 | {
172 | try
173 | {
174 | if (token == null || token.IsCancellationRequested) return true;
175 | return false;
176 | }
177 | catch
178 | {
179 | return true;
180 | }
181 | }
182 | }
183 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HttpLib 便捷的Http库 | 多线程下载库
2 |
3 | 如果你喜欢 HttpLib 项目,请为本项点亮一颗星 ⭐!
4 |
5 |
6 | [](https://www.nuget.org/packages/Tom.HttpLib)
7 | [](https://www.nuget.org/packages/Tom.HttpLib)
8 |
9 | ## 🖥支持环境
10 | - .NET 6.0及以上。
11 | - .NET Core3.1及以上。
12 | - .NET Standard2.0及以上。
13 |
14 | ## 🌴支持
15 |
16 | #### multipart/form-data
17 |
18 | 既可以上传文件等二进制数据,也可以上传表单键值对
19 |
20 | #### 上传与下载进度回调
21 |
22 | 上传与下载的进度监控
23 |
24 | #### 支持缓存
25 |
26 | 类似图片加载场景,同一个id的图片通过磁盘存储减少网络开支
27 |
28 |
29 | ****
30 |
31 | ## 目录
32 | * [示例](#示例)
33 | * [创建请求](#创建请求)
34 | * [添加参数](#添加参数)
35 | * [添加请求头](#添加请求头)
36 | * [设置代理](#设置代理)
37 | * [启用重定向](#启用重定向)
38 | * [设置超时时长](#设置超时时长)
39 | * [设置编码](#设置编码)
40 | * [设置缓存](#设置缓存)
41 | * [请求之前处理](#请求之前处理)
42 | * [注入回调获取进度](#注入回调获取进度)
43 | * [上传](#上传)
44 | * [下载](#下载)
45 | * [请求](#请求)
46 | * [异步错误](#异步错误)
47 | * [异步请求](#异步请求)
48 | * [同步获取](#同步获取)
49 | * [实例](#实例)
50 | * [实例下载文件](#实例下载文件)
51 | * [实例流式传输](#实例流式传输)
52 | * [实例获取域名IP](#实例获取域名IP)
53 | * [实例全局错误捕获](#实例全局错误捕获)
54 | * [ResultResponse介绍](#ResultResponse介绍)
55 |
56 |
57 | # 示例
58 | ## 创建请求
59 | ``` csharp
60 | Http.Get("https://www.baidu.com")
61 | ```
62 | ``` csharp
63 | Http.Post("https://www.baidu.com")
64 | ```
65 | ``` csharp
66 | Http.Put("https://www.baidu.com")
67 | ```
68 | ``` csharp
69 | Http.Delete("https://www.baidu.com")
70 | ```
71 | ### 添加参数
72 |
73 | > GET请求参数会自动注入到地址
74 |
75 | ``` csharp
76 | data("wd", "随便搜一下")
77 | data(new { test1 = "测试1", test2 = "测试2" })
78 | data(new { params_ = "关键字参数" })
79 | data(new { wd = new string[] { "GitHub - Haku-Men HttpLib", "POST数组参数" } })
80 | ```
81 |
82 | > URL参数(除了GET请求)
83 |
84 | ``` csharp
85 | query("键", "值对")
86 | query(new { test = "POST下继续传递URL参数" })
87 | ```
88 |
89 | ``` csharp
90 | data(new Val("test1", "测试1"), new Val("test2", "测试2"))
91 | ```
92 | ``` csharp
93 | data(new List {
94 | new Val("test1","测试1"),
95 | new Val("test2","测试2")
96 | })
97 | ```
98 |
99 | > 上传字符串 默认 `text/plain`
100 |
101 | ``` csharp
102 | string json = "{\"JSON\":\"json data\"}";
103 | datastr(json, "application/json")
104 | ```
105 |
106 | > 上传文件
107 |
108 | ``` csharp
109 | data(new Files("文件地址"))
110 | ```
111 | ``` csharp
112 | file(@"文件地址")
113 | ```
114 |
115 | ### 添加请求头
116 | ``` csharp
117 | header("Authorization", "abc")
118 | ```
119 | ``` csharp
120 | header(new { accept = "*/*", userAgent = "Chrome" })
121 | ```
122 | ``` csharp
123 | header(new Val("accept","*/*"), new Val("user-agent","Chrome"))
124 | ```
125 |
126 | ### 设置代理
127 | ``` csharp
128 | proxy("127.0.0.1",1000)
129 | ```
130 |
131 | ### 启用重定向
132 | >默认禁止
133 | ``` csharp
134 | redirect()
135 | ```
136 |
137 | ### 设置超时时长
138 | >`毫秒`(默认不超时)
139 | ``` csharp
140 | timeout(3000)
141 | ```
142 |
143 | ### 设置编码
144 | >默认`utf-8`
145 | ``` csharp
146 | encoding('utf-8')
147 | ```
148 |
149 | ## 设置缓存
150 | >先配置`Config.CacheFolder`缓存文件夹
151 | ``` csharp
152 | cache("缓存id")
153 | ```
154 | >或者设定有效期 1分钟
155 | ``` csharp
156 | cache("缓存id",1)
157 | ```
158 |
159 | ### 请求之前处理
160 | ``` csharp
161 | before((HttpWebResponse response, ResultResponse result) =>
162 | {
163 | return true; //继续请求
164 | })
165 | ```
166 |
167 | ### 注入回调获取进度
168 | >字节大小
169 | #### 上传
170 | ``` csharp
171 | requestProgres((bytesSent, totalBytes) => {
172 | double prog = (bytesSent * 1.0) / (totalBytes * 1.0);
173 | Console.Write("{0}% 上传", Math.Round(prog * 100.0, 1).ToString("N1"));
174 | })
175 | ```
176 | #### 下载
177 | ``` csharp
178 | responseProgres((bytesSent, totalBytes) => {
179 | if (totalBytes > 0)
180 | {
181 | double prog = (bytesSent * 1.0) / (totalBytes * 1.0);
182 | Console.Write("{0}% 下载", Math.Round(prog * 100.0, 1).ToString("N1"));
183 | }
184 | })
185 | ```
186 |
187 | ## 请求
188 | ### 异步错误
189 | ``` csharp
190 | fail((ResultResponse result) => {
191 | })
192 | ```
193 | ### 同步获取
194 | ``` csharp
195 | requestNone();//不下载流
196 | request();//返回字符串
197 | requestData();//返回字节
198 | download("保存目录", "保存文件名称(为空自动获取)");//下载文件
199 | ```
200 |
201 | # 实例
202 |
203 | ``` csharp
204 | string result = Http.Get("https://www.baidu.com/s")
205 | .data(new { wd = "GitHub - Haku-Men HttpLib", params_ = "关键字参数" })
206 | .redirect()
207 | .request();
208 | Console.Write(result);
209 | ```
210 |
211 | # 实例下载文件
212 | ``` csharp
213 | var savapath = Http.Get("https://dldir1.qq.com/qqfile/qq/QQNT/Windows/QQ_9.9.9_240422_x64_01.exe")
214 | .redirect()
215 | .responseProgres((bytesSent, totalBytes) =>
216 | {
217 | Console.SetCursorPosition(0, 0);
218 | if (totalBytes > 0)
219 | {
220 | double prog = (bytesSent * 1.0) / (totalBytes * 1.0);
221 | Console.Write("{0}% 下载 {1}/{2} ", Math.Round(prog * 100.0, 1).ToString("N1"), CountSize(bytesSent), CountSize(totalBytes));
222 | }
223 | else
224 | {
225 | Console.Write("{0} 下载 ", CountSize(bytesSent));
226 | }
227 | }).download(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "qq.exe");
228 | if (savapath != null) Console.WriteLine("下载成功保存至:" + savapath);
229 | else Console.WriteLine("下载失败");
230 | ```
231 |
232 | # 实例流式传输
233 | ``` csharp
234 | Http.Get("https://test.chatgpt.com/local_doc_chat").request(msg =>
235 | {
236 | if (msg == null) return;
237 | if (msg.StartsWith("data: ")) msg = msg.Substring(5).Trim();
238 | System.Diagnostics.Debug.WriteLine(msg);
239 | });
240 | ```
241 |
242 | # 实例获取域名IP
243 | ``` csharp
244 | Http.Get("https://www.baidu.com").IP
245 | ```
246 |
247 | # 实例全局错误捕获
248 | ``` csharp
249 | Config.fail += (HttpCore core, ResultResponse result)=>
250 | {
251 | if (result.Exception == null) return;
252 | Console.Write(err.GetType());
253 | Console.Write(err.Message);
254 | };
255 | ```
256 |
257 | # ResultResponse介绍
258 |
259 | |代码|类型|解释|说明|
260 | |:--|:--|:--:|:--|
261 | |StatusCode|int|状态代码|`200` 为正常 常见的有`404`未找到、`302`重定向、`502`网址报错|
262 | |IsSuccessStatusCode|bool|响应是否成功|range `200`-`299`|
263 | |ServerHeader|string`?`|服务头|HTTP 200 OK BWS/1.1 Ver:1.1|
264 | |Uri|Uri|最终的地址||
265 | |Type|string`?`|服务指示类型|`Content-Type`|
266 | |Header|Dictionary|响应头||
267 | |Cookie|Dictionary|Cookie||
268 | |OriginalSize|long|流原始大小|动态压缩|
269 | |Size|long|流大小||
270 | |Exception|Exception`?`|异常信息||
--------------------------------------------------------------------------------
/examples/TDown/Form1.cs:
--------------------------------------------------------------------------------
1 | using HttpLib;
2 | using System;
3 | using System.Text;
4 | using System.Windows.Forms;
5 |
6 | namespace TDown
7 | {
8 | public partial class Form1 : AntdUI.BaseForm
9 | {
10 | public Form1()
11 | {
12 | InitializeComponent();
13 | }
14 | HttpDown down;
15 | private void btn_Click(object sender, EventArgs e)
16 | {
17 | progress.Value = 0;
18 | txt_state.State = AntdUI.TState.Default;
19 | txt_state.Text = "待下载";
20 | txt_speed.Text = txt_time.Text = txt_max.Text = txt_value.Text = txt_start_time.Text = txt_end_time.Text = null;
21 | int maxThreads = Environment.ProcessorCount;
22 | string downUrl = txt_uri.Text;
23 | if (int.TryParse(textBox2.Text, out int taskCount))
24 | {
25 | progress.Loading = true;
26 | DateTime start_time = DateTime.Now;
27 | txt_start_time.Text = start_time.ToString("HH:mm:ss");
28 | down = Http.Get(downUrl).redirect().downLoad(Program.BasePath, MD5Encrypt16(downUrl));
29 | btn.Enabled = btn_resume.Enabled = false;
30 | btn_suspend.Enabled = btn_stop.Enabled = true;
31 | down.ValueChange(t =>
32 | {
33 | double prog = (t * 1.0) / down.MaxValue;
34 | if (down.MaxValue > t)
35 | {
36 | progress.Value = (float)prog;
37 | txt_prog.Text = Math.Round(prog * 100.0, 1) + "%";
38 | }
39 | txt_value.Text = ByteUnit(t);
40 | });
41 | down.MaxValueChange(t =>
42 | {
43 | if (t > 0)
44 | {
45 | txt_prog.Text = Math.Round(down.Value / t * 100.0, 1) + "%";
46 | txt_max.Text = ByteUnit(t);
47 | }
48 | else
49 | {
50 | txt_prog.Text = "∞";
51 | txt_max.Text = "未知";
52 | }
53 | });
54 | down.StateChange((t, err) =>
55 | {
56 | switch (t)
57 | {
58 | case DownState.Complete:
59 | txt_state.State = AntdUI.TState.Success;
60 | txt_state.Text = "完成 " + err;
61 | break;
62 | case DownState.Downloading:
63 | txt_state.State = AntdUI.TState.Processing;
64 | txt_state.Text = "下载中";
65 | break;
66 | case DownState.Fail:
67 | txt_state.State = AntdUI.TState.Error;
68 | txt_state.Text = "异常";
69 | break;
70 | case DownState.Stop:
71 | txt_state.State = AntdUI.TState.Warn;
72 | txt_state.Text = "已停止 " + err;
73 | break;
74 | }
75 | });
76 | down.TimeChange(t =>
77 | {
78 | txt_time.Text = t;
79 | });
80 | down.SpeedChange(t =>
81 | {
82 | txt_start_time.Text = start_time.ToString("HH:mm:ss") + " | 耗时 " + Math.Round((DateTime.Now - start_time).TotalSeconds) + "秒";
83 | txt_speed.Text = ByteUnit(t);
84 | });
85 | down.Go(taskCount).ContinueWith((action =>
86 | {
87 | progress.Loading = false;
88 | DateTime end_time = DateTime.Now;
89 | txt_start_time.Text = start_time.ToString("HH:mm:ss");
90 | txt_end_time.Text = end_time.ToString("HH:mm:ss") + " | 耗时 " + Math.Round((end_time - start_time).TotalSeconds) + "秒";
91 | Invoke(new Action(() =>
92 | {
93 | btn_stop.Enabled = btn_suspend.Enabled = btn_resume.Enabled = false;
94 | btn.Enabled = true;
95 | }));
96 | System.Diagnostics.Debug.WriteLine("保存至:" + action.Result);
97 | if (action.Result != null)
98 | MessageBox.Show("保存至:" + action.Result);
99 | }));
100 | }
101 | }
102 |
103 | private void btn_suspend_Click(object sender, EventArgs e)
104 | {
105 | btn_suspend.Enabled = false;
106 | btn_resume.Enabled = true;
107 | down.Suspend();
108 | }
109 |
110 | private void btn_resume_Click(object sender, EventArgs e)
111 | {
112 | btn_resume.Enabled = false;
113 | btn_suspend.Enabled = true;
114 | down.Resume();
115 | }
116 |
117 | private void btn_stop_Click(object sender, EventArgs e)
118 | {
119 | down.Dispose();
120 | btn_stop.Enabled = btn_suspend.Enabled = btn_resume.Enabled = false;
121 | btn.Enabled = true;
122 | }
123 |
124 | #region 转换
125 |
126 | public static string ByteUnit(long val, int d = 1, string nul = "0B")
127 | {
128 | return ByteUnit(val * 1.0, d, nul);
129 | }
130 | public static string ByteUnit(double val, int d = 1, string nul = "0B")
131 | {
132 | if (val == 0) return nul;
133 | var _val = val;
134 | int unit = 0;
135 | while (_val > 1024)
136 | {
137 | _val /= 1024;
138 | unit++;
139 | if (unit > 5)
140 | {
141 | break;
142 | }
143 | }
144 | return Math.Round(_val, d) + CountSizeUnit(unit);
145 | }
146 |
147 | static string CountSizeUnit(int val)
148 | {
149 | switch (val)
150 | {
151 | case 4: return "T";
152 | case 3: return "G";
153 | case 2: return "M";
154 | case 1: return "K";
155 | case 5: return "P";
156 | case 6: return "E";
157 | //case 7: return "Z";
158 | //case 8: return "Y";
159 | default: return "B";
160 | }
161 | }
162 |
163 | #endregion
164 |
165 | public static string MD5Encrypt16(string str)
166 | {
167 | using (var md5 = System.Security.Cryptography.MD5.Create())
168 | {
169 | return BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(str)), 4, 8).Replace("-", "").ToUpper();
170 | }
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/src/HttpDown/HttpDown.Option.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Threading;
5 |
6 | namespace HttpLib
7 | {
8 | partial class HttpDown
9 | {
10 | #region 参数
11 |
12 | ///
13 | /// 自定义ID
14 | ///
15 | public string? ID { get; set; }
16 |
17 | ///
18 | /// 下载地址
19 | ///
20 | public string Url { get; }
21 |
22 | ///
23 | /// 保存路径
24 | ///
25 | public string SavePath { get; }
26 |
27 | ///
28 | /// 超时时长
29 | ///
30 | public int TimeOut = Config.TimeOut;
31 |
32 | ///
33 | /// 重试次数
34 | ///
35 | public int RetryCount = Config.RetryCount;
36 |
37 | ///
38 | /// 下载缓存大小
39 | ///
40 | public int CacheSize = Config.CacheSize;
41 |
42 | ///
43 | /// 下载速度
44 | ///
45 | public double Speed { get => _Speed; }
46 |
47 | ///
48 | /// 剩余时间
49 | ///
50 | public string? Time { get => _Time; }
51 |
52 | ///
53 | /// 当前下载值
54 | ///
55 | public long Value { get => _Value; }
56 |
57 | ///
58 | /// 文件大小
59 | ///
60 | public long MaxValue { get => _MaxValue; }
61 |
62 | ///
63 | /// 任务状态
64 | ///
65 | public DownState State { get => _State; }
66 |
67 |
68 | #region 非公开
69 |
70 | DownState _State = DownState.Ready;
71 | long _MaxValue, _Value, _Speed;
72 | string? _Time;
73 |
74 | long[] ValTmp = new long[0];
75 | long[] MaxTmp = new long[0];
76 | internal ManualResetEvent resetState = new ManualResetEvent(true);
77 |
78 | #endregion
79 |
80 | #endregion
81 |
82 | #region 任务事件
83 |
84 | public delegate void StateHandler(DownState e, string err);
85 |
86 | #region 文件大小
87 |
88 | int DownCount = 0, TotalCount = 0;
89 |
90 | Action? _MaxValueChange;
91 | ///
92 | /// 文件大小改变
93 | ///
94 | public HttpDown MaxValueChange(Action action)
95 | {
96 | _MaxValueChange = action;
97 | return this;
98 | }
99 |
100 | void SetMaxValue(long value)
101 | {
102 | if (_MaxValue == value) return;
103 | _MaxValue = value;
104 | _MaxValueChange?.Invoke(value);
105 | }
106 |
107 | void SetMaxValue()
108 | {
109 | var val = MaxTmp.Sum();
110 | if (TotalCount > 0) SetMaxValue(val * TotalCount / DownCount);
111 | else SetMaxValue(val);
112 | }
113 |
114 | internal void SetMaxValue(int i, long value)
115 | {
116 | if (MaxTmp[i] == value) return;
117 | MaxTmp[i] = value;
118 | var val = MaxTmp.Sum();
119 | if (TotalCount > 0) SetMaxValue(val * TotalCount / DownCount);
120 | else SetMaxValue(val);
121 | }
122 |
123 | #endregion
124 |
125 | #region 当前下载值
126 |
127 | Action? _ValueChange;
128 | ///
129 | /// 当前下载值改变
130 | ///
131 | public HttpDown ValueChange(Action action)
132 | {
133 | _ValueChange = action;
134 | return this;
135 | }
136 |
137 | void SetValue(long value)
138 | {
139 | if (_Value == value) return;
140 | _Value = value;
141 | _ValueChange?.Invoke(value);
142 | }
143 |
144 | internal void SetValue(int i, long value)
145 | {
146 | ValTmp[i] = value;
147 | SetValue(ValTmp.Sum());
148 | }
149 |
150 | #endregion
151 |
152 | #region 下载速度
153 |
154 | Action? _SpeedChange;
155 | ///
156 | /// 下载速度改变
157 | ///
158 | public HttpDown SpeedChange(Action action)
159 | {
160 | _SpeedChange = action;
161 | return this;
162 | }
163 |
164 | internal bool CanSpeed = true;
165 | internal void SetSpeed(long value)
166 | {
167 | if (!CanSpeed) return;
168 | if (_Speed == value) return;
169 | _Speed = value;
170 | _SpeedChange?.Invoke(value);
171 | }
172 |
173 | #endregion
174 |
175 | #region 剩余时间
176 |
177 | Action? _TimeChange;
178 | ///
179 | /// 剩余时间改变
180 | ///
181 | public HttpDown TimeChange(Action action)
182 | {
183 | _TimeChange = action;
184 | return this;
185 | }
186 |
187 | internal void SetTime(string? value)
188 | {
189 | if (!CanSpeed) return;
190 | if (_Time == value) return;
191 | _Time = value;
192 | _TimeChange?.Invoke(value);
193 | }
194 |
195 | #endregion
196 |
197 | #region 任务状态
198 |
199 | Action? _StateChange;
200 | ///
201 | /// 下载状态改变
202 | ///
203 | public HttpDown StateChange(Action action)
204 | {
205 | _StateChange = action;
206 | return this;
207 | }
208 |
209 | internal void SetState(DownState value, string? err = null)
210 | {
211 | if (_State == value) return;
212 | _State = value;
213 | _StateChange?.Invoke(value, err);
214 | }
215 |
216 | #endregion
217 |
218 | #endregion
219 |
220 | #region 初始化
221 |
222 | public Uri Uri;
223 | public HttpCore core;
224 | public HttpDown(HttpCore _core, string _savePath, string? id = null)
225 | {
226 | core = _core;
227 | Uri = core.option.uri;
228 | ID = id;
229 | SavePath = _savePath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
230 | Url = core.option.uri.AbsoluteUri;
231 | }
232 |
233 | #endregion
234 | }
235 |
236 | public enum DownState
237 | {
238 | ///
239 | /// 准备中
240 | ///
241 | Ready,
242 | ///
243 | /// 下载中
244 | ///
245 | Downloading,
246 | ///
247 | /// 已停止
248 | ///
249 | Stop,
250 | ///
251 | /// 完成
252 | ///
253 | Complete,
254 | ///
255 | /// 异常
256 | ///
257 | Fail
258 | }
259 | }
--------------------------------------------------------------------------------
/src/Core/Http.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 |
5 | namespace HttpLib
6 | {
7 | ///
8 | /// 快捷的HTTP请求
9 | ///
10 | public static class Http
11 | {
12 | #region SSL
13 |
14 | static Http()
15 | {
16 | #if NET40
17 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
18 | #elif NET45 || NET46 || NETSTANDARD2_0 || NETSTANDARD2_1
19 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
20 | #elif NET48
21 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
22 | #else
23 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
24 | #endif
25 | }
26 |
27 | #endregion
28 |
29 | ///
30 | /// GET 请求
31 | ///
32 | /// 地址
33 | public static HttpCore Get(this string url) => Core(url, HttpMethod.Get);
34 |
35 | ///
36 | /// GET 请求
37 | ///
38 | /// 地址
39 | public static HttpCore Get(this Uri url) => Core(url, HttpMethod.Get);
40 |
41 | ///
42 | /// POST 请求
43 | ///
44 | /// 地址
45 | public static HttpCore Post(this string url) => Core(url, HttpMethod.Post);
46 |
47 | ///
48 | /// POST 请求
49 | ///
50 | /// 地址
51 | public static HttpCore Post(this Uri url) => Core(url, HttpMethod.Post);
52 |
53 | ///
54 | /// Put 请求
55 | ///
56 | /// 地址
57 | public static HttpCore Put(this string url) => Core(url, HttpMethod.Put);
58 |
59 | ///
60 | /// Put 请求
61 | ///
62 | /// 地址
63 | public static HttpCore Put(this Uri url) => Core(url, HttpMethod.Put);
64 |
65 | ///
66 | /// Delete 请求
67 | ///
68 | /// 地址
69 | public static HttpCore Delete(this string url) => Core(url, HttpMethod.Delete);
70 |
71 | ///
72 | /// Delete 请求
73 | ///
74 | /// 地址
75 | public static HttpCore Delete(this Uri url) => Core(url, HttpMethod.Delete);
76 |
77 | public static HttpCore Core(this string url, HttpMethod method) => new HttpCore(url, method);
78 | public static HttpCore Core(this Uri url, HttpMethod method) => new HttpCore(url, method);
79 |
80 | #region 缓存
81 |
82 | public static string? Cache(this string ID, int time = 0)
83 | {
84 | var file = Config.CacheFolder + ID;
85 | if (System.IO.File.Exists(Config.CacheFolder + ID))
86 | {
87 | if (time > 0)
88 | {
89 | var t = System.IO.File.GetCreationTime(file);
90 | var elapsedTicks = DateTime.Now.Ticks - t.Ticks;
91 | var elapsedSpan = new TimeSpan(elapsedTicks);
92 | if (elapsedSpan.TotalMinutes < time) return System.IO.File.ReadAllText(file);
93 | }
94 | else return System.IO.File.ReadAllText(file);
95 | }
96 | return null;
97 | }
98 | public static byte[]? CacheData(this string ID, int time = 0)
99 | {
100 | var file = Config.CacheFolder + ID;
101 | if (System.IO.File.Exists(Config.CacheFolder + ID))
102 | {
103 | if (time > 0)
104 | {
105 | var t = System.IO.File.GetCreationTime(file);
106 | var elapsedTicks = DateTime.Now.Ticks - t.Ticks;
107 | var elapsedSpan = new TimeSpan(elapsedTicks);
108 | if (elapsedSpan.TotalDays < time) return System.IO.File.ReadAllBytes(file);
109 | }
110 | else return System.IO.File.ReadAllBytes(file);
111 | }
112 | return null;
113 | }
114 |
115 | #endregion
116 | }
117 |
118 | ///
119 | /// 响应结果
120 | ///
121 | public class ResultResponse
122 | {
123 | public ResultResponse(Uri uri)
124 | {
125 | Uri = uri;
126 | OriginalSize = Size = -1;
127 | Header = Cookie = new Dictionary(0);
128 | }
129 | public ResultResponse(Uri uri, Exception ex) : this(uri) { Exception = ex; }
130 | public ResultResponse(HttpWebResponse data, Exception ex) : this(data) { Exception = ex; }
131 | public ResultResponse(HttpWebResponse data)
132 | {
133 | OriginalSize = Size = data.ContentLength;
134 | StatusCode = (int)data.StatusCode;
135 | Type = data.ContentType;
136 | ServerHeader = string.Join(" ", new string[] {
137 | data.ResponseUri.Scheme.ToUpper(),
138 | StatusCode.ToString(),
139 | data.StatusCode.ToString(),
140 | data.Server,
141 | "Ver:" + data.ProtocolVersion.ToString()
142 | });
143 | Uri = data.ResponseUri;
144 | if (data.Headers.Count > 0 || data.Cookies.Count > 0)
145 | {
146 | if (data.Headers.Count > 0)
147 | {
148 | var header = new Dictionary>(data.Headers.AllKeys.Length);
149 | foreach (string it in data.Headers.AllKeys)
150 | {
151 | var val = data.Headers[it];
152 | if (val == null) continue;
153 | if (header.ContainsKey(it)) header[it].Add(val);
154 | else header.Add(it, new List { val });
155 | }
156 | Header = new Dictionary(header.Count);
157 | foreach (var it in header) Header.Add(it.Key, string.Join("; ", it.Value));
158 | }
159 | else Header = new Dictionary(0);
160 |
161 | if (data.Cookies.Count > 0)
162 | {
163 | var cookie = new Dictionary>(data.Cookies.Count);
164 | foreach (Cookie it in data.Cookies)
165 | {
166 | if (cookie.ContainsKey(it.Name)) cookie[it.Name].Add(it.Value);
167 | else cookie.Add(it.Name, new List { it.Value });
168 | }
169 | Cookie = new Dictionary(cookie.Count);
170 | foreach (var it in cookie) Cookie.Add(it.Key, string.Join(";", it.Value));
171 | }
172 | else Cookie = new Dictionary(0);
173 | }
174 | else Header = Cookie = new Dictionary(0);
175 | }
176 |
177 | ///
178 | /// 指示HTTP响应是否成功 range 200-299
179 | ///
180 | public bool IsSuccessStatusCode { get => StatusCode >= 200 && StatusCode <= 299; }
181 |
182 | ///
183 | /// 状态代码
184 | ///
185 | public int StatusCode { get; set; }
186 |
187 | ///
188 | /// 服务头
189 | ///
190 | public string? ServerHeader { get; set; }
191 |
192 | ///
193 | /// 地址
194 | ///
195 | public Uri Uri { get; set; }
196 |
197 | ///
198 | /// 响应指示类型
199 | ///
200 | public string? Type { get; set; }
201 |
202 | ///
203 | /// 响应头
204 | ///
205 | public Dictionary Header { get; set; }
206 | public Dictionary Cookie { get; set; }
207 |
208 | ///
209 | /// 流原始大小
210 | ///
211 | public long OriginalSize { get; set; }
212 |
213 | ///
214 | /// 流大小
215 | ///
216 | public long Size { get; set; }
217 |
218 | ///
219 | /// 错误异常
220 | ///
221 | public Exception? Exception { get; set; }
222 | }
223 | }
--------------------------------------------------------------------------------
/src/HttpDown/HttpDown.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace HttpLib
10 | {
11 | public partial class HttpDown : IDisposable
12 | {
13 | #region 下载
14 |
15 | #region 多样下载
16 |
17 | ///
18 | /// 下载-自动
19 | ///
20 | public Task Go() => Go(Environment.ProcessorCount, null);
21 |
22 | ///
23 | /// 下载-自定义下载线程数
24 | ///
25 | public Task Go(int ThreadCount) => Go(ThreadCount, null);
26 |
27 | ///
28 | /// 下载-自定义保存文件名称
29 | ///
30 | ///
31 | public Task Go(string FileName) => Go(Environment.ProcessorCount, FileName);
32 |
33 | #endregion
34 |
35 | ///
36 | /// 下载
37 | ///
38 | /// 线程数
39 | /// 文件名称
40 | public Task Go(int ThreadCount, string? FileName)
41 | {
42 | CanSpeed = true;
43 | #if NET40
44 | return Task.Factory.StartNew(() =>
45 | {
46 | return DownLoad(ThreadCount, FileName);
47 | });
48 | #else
49 | return Task.Run(() =>
50 | {
51 | return DownLoad(ThreadCount, FileName);
52 | });
53 | #endif
54 | }
55 |
56 | #endregion
57 |
58 | #region 功能
59 |
60 | ///
61 | /// 暂停下载
62 | ///
63 | public void Suspend()
64 | {
65 | resetState.Reset();
66 | SetState(DownState.Stop);
67 | }
68 | ///
69 | /// 恢复下载
70 | ///
71 | public void Resume()
72 | {
73 | SetState(DownState.Downloading);
74 | resetState.Set();
75 | }
76 |
77 | #endregion
78 |
79 | public override string ToString() => Url;
80 |
81 | ///
82 | /// 停止下载
83 | ///
84 | public void Dispose()
85 | {
86 | //终止task线程
87 | resetState.Set();
88 | resetState.Dispose();
89 | }
90 |
91 | #region 核心
92 |
93 | string? DownLoad(int ThreadCount, string? FileName)
94 | {
95 | string WorkPath = SavePath + (ID ?? Guid.NewGuid().ToString()) + Path.DirectorySeparatorChar;
96 | WorkPath.CreateDirectory();
97 | long Length = HttpDownLib.PreRequest(this, ThreadCount, out bool can_range, out var disposition);
98 | FileName ??= Uri.FileName(disposition);
99 | TotalCount = DownCount = 0;
100 |
101 | #region 任务分配
102 |
103 | long SingleFileLength = Length;
104 | int TaskCount = 1;
105 | if (can_range && Length > 2097152)
106 | {
107 | SingleFileLength = 2097152;//大于2MB才做切片
108 | TaskCount = (int)Math.Ceiling(Length / (SingleFileLength * 1.0));//任务分块
109 | }
110 |
111 | var files = new List(TaskCount);
112 | List valTmp = new List(TaskCount), maxTmp = new List(TaskCount);
113 | for (int i = 0; i < TaskCount; i++)
114 | {
115 | long byte_start = SingleFileLength * i, byte_end = SingleFileLength;
116 | if ((byte_start + SingleFileLength) > Length) byte_end = SingleFileLength - ((byte_start + SingleFileLength) - Length);
117 |
118 | string filename_tmp = $"{i}_{byte_start}_{byte_start + byte_end}.temp";
119 | files.Add(new FilesResult(i, WorkPath + filename_tmp, byte_start, byte_end));
120 | valTmp.Add(0);
121 | maxTmp.Add(byte_end);
122 | }
123 | ValTmp = valTmp.ToArray();
124 | MaxTmp = maxTmp.ToArray();
125 |
126 | SetMaxValue();
127 |
128 | #endregion
129 |
130 | var option = new HttpDownOption(this, ThreadCount, FileName, SavePath, WorkPath, can_range);
131 | return HttpDownLib.DownLoad(option, files.ToArray());
132 | }
133 |
134 | #endregion
135 | }
136 |
137 | internal class HttpDownLib
138 | {
139 | #region 核心
140 |
141 | public static string? DownLoad(HttpDownOption option, FilesResult[] fileRS)
142 | {
143 | option.core.core.range();
144 | option.core.SetState(DownState.Downloading, null);
145 | option.core.CanSpeed = true;
146 | TestTime(option);
147 |
148 | bool is_stop = false;
149 | Parallel.ForEach(fileRS, new ParallelOptions { MaxDegreeOfParallelism = option.ThreadCount }, it => DownLoadSingleRetry(option, it, ref is_stop));
150 | option.core.CanSpeed = false;
151 | if (is_stop) option.core.SetState(DownState.Complete, "主动停止");
152 | else
153 | {
154 | var errors = new List(1);
155 | int errorcount = 0;
156 | foreach (var it in fileRS)
157 | {
158 | if (it.error)
159 | {
160 | errorcount++;
161 | if (it.errmsg != null && !errors.Contains(it.errmsg)) errors.Add(it.errmsg);
162 | }
163 | }
164 | if (errorcount > 0)
165 | {
166 | if (errors.Count > 0) option.core.SetState(DownState.Fail, string.Join(" ", errors));
167 | else option.core.SetState(DownState.Fail, "下载不完全");
168 | return null;
169 | }
170 | var files = new List(fileRS.Length);
171 | foreach (var it in fileRS) files.Add(it.path);
172 | try
173 | {
174 | var path = files.CombineMultipleFilesIntoSingleFile(option.SaveFullName, option.WorkPath);
175 | option.core.SetState(DownState.Complete, null);
176 | return path;
177 | }
178 | catch (Exception ez) { option.core.SetState(DownState.Fail, ez.Message); return null; }
179 | }
180 | return null;
181 | }
182 |
183 | static bool DownLoadSingleRetry(HttpDownOption option, FilesResult it, ref bool is_stop)
184 | {
185 | int ErrCount = 0;
186 | while (true)
187 | {
188 | if (option.core.resetState.Wait())
189 | {
190 | is_stop = true;
191 | option.core.SetState(DownState.Complete, "主动停止");
192 | return false;
193 | }
194 | if (DownLoadSingle(option, it, ref is_stop, out var err))
195 | {
196 | it.error = false;
197 | it.errmsg = null;
198 | return true;
199 | }
200 | else
201 | {
202 | ErrCount++;
203 | it.errmsg = err;
204 | it.error = true;
205 | if (ErrCount > option.core.RetryCount) return false;
206 | else Thread.Sleep(1000);
207 | }
208 | }
209 | }
210 | static bool DownLoadSingle(HttpDownOption option, FilesResult item, ref bool is_stop, out string? error)
211 | {
212 | error = null;
213 | try
214 | {
215 | long PreFileLength = 0;
216 | using (var file = new FileStream(item.path, FileMode.OpenOrCreate))
217 | {
218 | if (item.end_position > 0 && file.Length >= item.end_position)
219 | {
220 | PreFileLength = item.end_position;
221 | option.core.SetMaxValue(item.i, PreFileLength);
222 | option.core.SetValue(item.i, PreFileLength);
223 | return true;
224 | }
225 | else if (option.CanRange) PreFileLength = file.Length;
226 | else
227 | {
228 | file.Close();
229 | File.Delete(item.path);
230 | }
231 | }
232 |
233 | using (var file = new FileStream(item.path, FileMode.OpenOrCreate))
234 | {
235 | var request = option.core.core.CreateRequest();
236 | if (option.CanRange) request.AddRange(item.start_position + PreFileLength, item.start_position + item.end_position);
237 | using (var response = (HttpWebResponse)request.GetResponse())
238 | {
239 | if (response.ContentLength > 0) option.core.SetMaxValue(item.i, PreFileLength + response.ContentLength);
240 | using (var stream = response.GetResponseStream())
241 | {
242 | long DownValue = 0;
243 | if (PreFileLength > 0)
244 | {
245 | file.Seek(PreFileLength, SeekOrigin.Begin);
246 | DownValue += PreFileLength;
247 | option.core.SetValue(item.i, DownValue);
248 | }
249 | byte[] cache = new byte[option.core.CacheSize];
250 | int osize = stream.Read(cache, 0, cache.Length);
251 | while (osize > 0)
252 | {
253 | DownValue += osize;
254 | option.core.SetValue(item.i, DownValue);
255 | if (option.core.resetState.Wait())
256 | {
257 | is_stop = true;
258 | return false;
259 | }
260 | file.Write(cache, 0, osize);
261 | osize = stream.Read(cache, 0, cache.Length);
262 | }
263 | option.core.SetValue(item.i, DownValue);
264 | }
265 | }
266 | }
267 | return true;
268 | }
269 | catch (Exception err)
270 | {
271 | error = err.Message;
272 | }
273 | return false;
274 | }
275 |
276 | ///
277 | /// 计算速度
278 | ///
279 | static void TestTime(HttpDownOption option)
280 | {
281 | var tmp = new List();
282 | long oldsize = 0;
283 | ITask.Run(() =>
284 | {
285 | while (option.core.State == DownState.Downloading || option.core.State == DownState.Stop)
286 | {
287 | Thread.Sleep(1000);
288 | long _downsize = option.core.Value - oldsize;
289 | oldsize = option.core.Value;
290 |
291 | if (_downsize > 0)
292 | {
293 | int se = (int)((option.core.MaxValue - oldsize) / _downsize);
294 | if (se < 1)
295 | {
296 | option.core.SetTime(null);
297 | option.core.SetSpeed(_downsize);
298 | }
299 | else
300 | {
301 | tmp.Add(se);
302 |
303 | if (tmp.Count > 4)
304 | {
305 | int AVE = (int)Math.Ceiling(tmp.Average());
306 | tmp.Clear();
307 | TimeSpan timeSpan = new TimeSpan(0, 0, 0, AVE);
308 | string time_txt = $"{timeSpan.Hours.ToString().PadLeft(2, '0')}:{timeSpan.Minutes.ToString().PadLeft(2, '0')}:{timeSpan.Seconds.ToString().PadLeft(2, '0')}";
309 | if (time_txt.StartsWith("00:")) time_txt = time_txt.Substring(3);
310 | option.core.SetTime(time_txt);
311 | }
312 | option.core.SetSpeed(_downsize);
313 | }
314 | }
315 | else option.core.SetSpeed(0);
316 | }
317 | });
318 | }
319 |
320 | ///
321 | /// 预请求
322 | ///
323 | ///
324 | /// 是否可以分段
325 | ///
326 | /// 真实长度
327 | public static long PreRequest(HttpDown core, int ThreadCount, out bool can_range, out string? disposition)
328 | {
329 | disposition = null;
330 | try
331 | {
332 | core.core.range();
333 | var request = core.core.requestNone();
334 | if (request.Header.ContainsKey("Content-Disposition")) disposition = request.Header["Content-Disposition"];
335 | var ReadLength = request.Size;
336 | if (ThreadCount > 1 && ReadLength > 0)
337 | {
338 | try
339 | {
340 | core.core.range(1, ReadLength - 1);
341 | var request2 = core.core.requestNone();
342 | long length = request2.Size;
343 | can_range = length == ReadLength - 1;
344 | }
345 | catch
346 | {
347 | can_range = false;
348 | }
349 | }
350 | else can_range = false;
351 | return ReadLength;
352 | }
353 | catch
354 | {
355 | can_range = false;
356 | return 0;
357 | }
358 | }
359 |
360 | #endregion
361 | }
362 |
363 | internal class HttpDownOption
364 | {
365 | public HttpDownOption(HttpDown c, int threadCount, string fileName, string savePath, string workPath, bool can_range)
366 | {
367 | core = c;
368 | ThreadCount = threadCount;
369 | SaveFullName = savePath + fileName;
370 | WorkPath = workPath;
371 | CanRange = can_range;
372 | }
373 |
374 | public HttpDown core { get; set; }
375 |
376 | ///
377 | /// 线程数量
378 | ///
379 | public int ThreadCount { get; set; }
380 |
381 | public bool CanRange { get; set; }
382 |
383 | public string SaveFullName { get; set; }
384 | public string WorkPath { get; set; }
385 | }
386 |
387 | internal class FilesResult
388 | {
389 | public FilesResult(int _i, string _path, long s, long e)
390 | {
391 | i = _i;
392 | path = _path;
393 | start_position = s;
394 | end_position = e;
395 | }
396 | public int i { get; set; }
397 |
398 | ///
399 | /// 文件保存地址
400 | ///
401 | public string path { get; set; }
402 |
403 | ///
404 | /// 文件开始位置
405 | ///
406 | public long start_position { get; set; }
407 |
408 | ///
409 | /// 文件结束位置
410 | ///
411 | public long end_position { get; set; }
412 |
413 | public bool error { get; set; }
414 | public string? errmsg { get; set; }
415 | }
416 | }
--------------------------------------------------------------------------------
/src/HttpCore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 | using System.Text;
5 |
6 | namespace HttpLib
7 | {
8 | public partial class HttpCore
9 | {
10 | public HttpOption option;
11 | public HttpCore(string uri, HttpMethod method)
12 | {
13 | option = new HttpOption(uri, method);
14 | }
15 | public HttpCore(Uri uri, HttpMethod method)
16 | {
17 | option = new HttpOption(uri, method);
18 | }
19 | public HttpCore(HttpOption _option)
20 | {
21 | option = _option;
22 | }
23 |
24 | #region 请求参(GET)
25 |
26 | ///
27 | /// 请求参(GET)
28 | ///
29 | /// 多个参数
30 | public HttpCore query(params Val[] vals)
31 | {
32 | option.query ??= new List(vals.Length);
33 | option.query.AddRange(vals);
34 | return this;
35 | }
36 |
37 | ///
38 | /// 请求参(GET)
39 | ///
40 | /// 多个参数
41 | public HttpCore query(IList vals)
42 | {
43 | option.query ??= new List(vals.Count);
44 | option.query.AddRange(vals);
45 | return this;
46 | }
47 |
48 | ///
49 | /// 请求参(GET)
50 | ///
51 | /// 键
52 | /// 值
53 | public HttpCore query(string key, string? val)
54 | {
55 | if (option.query == null) option.query = new List { new Val(key, val) };
56 | else option.query.Add(new Val(key, val));
57 | return this;
58 | }
59 |
60 | ///
61 | /// 请求参(GET)
62 | ///
63 | /// 多个参数
64 | public HttpCore query(IDictionary vals)
65 | {
66 | option.query ??= new List(vals.Count);
67 | foreach (var it in vals) option.query.Add(new Val(it.Key, it.Value));
68 | return this;
69 | }
70 |
71 | ///
72 | /// 请求参(GET)
73 | ///
74 | /// 多个参数
75 | public HttpCore query(object data)
76 | {
77 | var list = data.GetType().GetProperties();
78 | var vals = new List(list.Length);
79 | foreach (var it in list)
80 | {
81 | string key = it.Name;
82 | if (key != "_" && key.EndsWith("_")) key = key.TrimEnd('_');
83 | var valO = it.GetValue(data, null);
84 | if (valO != null) vals.Add(new Val(key, valO.ToString()));
85 | }
86 | if (vals.Count > 0) return query(vals);
87 | return this;
88 | }
89 |
90 | #endregion
91 |
92 | #region 请求参
93 |
94 | ///
95 | /// 请求参
96 | ///
97 | /// 多个参数
98 | public HttpCore data(params Val[] vals)
99 | {
100 | option.data ??= new List(vals.Length);
101 | option.data.AddRange(vals);
102 | return this;
103 | }
104 |
105 | ///
106 | /// 请求参
107 | ///
108 | /// 多个参数
109 | public HttpCore data(IList vals)
110 | {
111 | option.data ??= new List(vals.Count);
112 | option.data.AddRange(vals);
113 | return this;
114 | }
115 |
116 | ///
117 | /// 请求参
118 | ///
119 | /// 键
120 | /// 值
121 | public HttpCore data(string key, string? val)
122 | {
123 | if (option.data == null) option.data = new List { new Val(key, val) };
124 | else option.data.Add(new Val(key, val));
125 | return this;
126 | }
127 |
128 | ///
129 | /// 请求参
130 | ///
131 | /// 多个参数
132 | public HttpCore data(IDictionary vals)
133 | {
134 | option.data ??= new List(vals.Count);
135 | foreach (var it in vals) option.data.Add(new Val(it.Key, it.Value));
136 | return this;
137 | }
138 |
139 | ///
140 | /// 请求参
141 | ///
142 | /// 多个参数
143 | public HttpCore data(object _data)
144 | {
145 | var list = _data.GetType().GetProperties();
146 | var vals = new List(list.Length);
147 | foreach (var it in list)
148 | {
149 | string key = it.Name;
150 | if (key != "_" && key.EndsWith("_")) key = key.TrimEnd('_');
151 | var valO = it.GetValue(_data, null);
152 | if (valO != null) vals.Add(new Val(key, valO.ToString()));
153 | }
154 | if (vals.Count > 0) return data(vals);
155 | return this;
156 | }
157 |
158 | ///
159 | /// 请求参(数组)
160 | ///
161 | /// 键
162 | /// 值
163 | public HttpCore data(string key, IList val)
164 | {
165 | if (option.method == HttpMethod.Get) throw new Exception("Get不支持数组");
166 | option.data ??= new List(val.Count);
167 | foreach (var it in val) option.data.Add(new Val(key + "[]", it));
168 | return this;
169 | }
170 |
171 | ///
172 | /// 请求参
173 | ///
174 | /// text/plain
175 | /// 类型
176 | public HttpCore datastr(string val, string contentType = "text/plain")
177 | {
178 | option.datastr = val;
179 | header("Content-Type", contentType);
180 | return this;
181 | }
182 |
183 | #region 文件
184 |
185 | ///
186 | /// 请求参(文件)
187 | ///
188 | /// 多个文件
189 | public HttpCore data(params Files[] files)
190 | {
191 | option.file ??= new List(files.Length);
192 | option.file.AddRange(files);
193 | return this;
194 | }
195 |
196 | ///
197 | /// 请求参(文件)
198 | ///
199 | /// 多个文件
200 | public HttpCore data(IList files)
201 | {
202 | option.file ??= new List(files.Count);
203 | option.file.AddRange(files);
204 | return this;
205 | }
206 |
207 | ///
208 | /// 请求参(文件)
209 | ///
210 | /// 多个文件
211 | public HttpCore file(params string[] vals)
212 | {
213 | option.file ??= new List(vals.Length);
214 | foreach (var item in vals) option.file.Add(new Files(item));
215 | return this;
216 | }
217 |
218 | ///
219 | /// 请求参(文件)
220 | ///
221 | /// 多个文件
222 | public HttpCore file(params Files[] vals)
223 | {
224 | option.file ??= new List(vals.Length);
225 | option.file.AddRange(vals);
226 | return this;
227 | }
228 |
229 | ///
230 | /// 请求参(文件)
231 | ///
232 | /// 多个文件
233 | public HttpCore file(IList vals)
234 | {
235 | option.file ??= new List(vals.Count);
236 | option.file.AddRange(vals);
237 | return this;
238 | }
239 |
240 | #endregion
241 |
242 | #endregion
243 |
244 | #region 请求头
245 |
246 | ///
247 | /// 请求头
248 | ///
249 | /// 多个参数
250 | public HttpCore header(params Val[] vals)
251 | {
252 | option.header ??= new List(vals.Length);
253 | option.header.AddRange(vals);
254 | return this;
255 | }
256 |
257 | ///
258 | /// 请求头
259 | ///
260 | /// 多个参数
261 | public HttpCore header(IList vals)
262 | {
263 | option.header ??= new List(vals.Count);
264 | option.header.AddRange(vals);
265 | return this;
266 | }
267 |
268 | ///
269 | /// 请求头
270 | ///
271 | /// 键
272 | /// 值
273 | public HttpCore header(string key, string? val)
274 | {
275 | if (option.header == null) option.header = new List { new Val(key, val) };
276 | else option.header.Add(new Val(key, val));
277 | return this;
278 | }
279 |
280 | ///
281 | /// 请求头
282 | ///
283 | /// 键
284 | /// 值
285 | public HttpCore header(string key, long val)
286 | {
287 | if (option.header == null) option.header = new List { new Val(key, val) };
288 | else option.header.Add(new Val(key, val));
289 | return this;
290 | }
291 |
292 | ///
293 | /// 请求头
294 | ///
295 | /// 多个参数
296 | public HttpCore header(IDictionary vals)
297 | {
298 | option.header ??= new List(vals.Count);
299 | foreach (var it in vals) option.header.Add(new Val(it.Key, it.Value));
300 | return this;
301 | }
302 |
303 | ///
304 | /// 请求头
305 | ///
306 | /// 多个参数
307 | public HttpCore header(object data)
308 | {
309 | var list = data.GetType().GetProperties();
310 | var vals = new List(list.Length);
311 | foreach (var it in list)
312 | {
313 | string key = GetTFName(it.Name).TrimStart('-');
314 | if (key != "_" && key.EndsWith("_")) key = key.TrimEnd('_');
315 | var valO = it.GetValue(data, null);
316 | if (valO != null) vals.Add(new Val(key, valO.ToString()));
317 | }
318 | if (vals.Count > 0) return header(vals);
319 | return this;
320 | }
321 |
322 | #endregion
323 |
324 | #region 代理
325 |
326 | ///
327 | /// 代理
328 | ///
329 | /// 服务器URI
330 | public HttpCore proxy(string address)
331 | {
332 | option.proxy = new WebProxy(address);
333 | return this;
334 | }
335 |
336 | ///
337 | /// 代理
338 | ///
339 | /// 服务器URI
340 | public HttpCore proxy(Uri address)
341 | {
342 | option.proxy = new WebProxy(address);
343 | return this;
344 | }
345 |
346 | ///
347 | /// 代理
348 | ///
349 | /// 主机名称
350 | /// 端口
351 | public HttpCore proxy(string host, int port)
352 | {
353 | option.proxy = new WebProxy(host, port);
354 | return this;
355 | }
356 |
357 | ///
358 | /// 代理
359 | ///
360 | /// 主机名称
361 | /// 端口
362 | /// 用户名
363 | /// 密码
364 | public HttpCore proxy(string host, int port, string? username, string? password)
365 | {
366 | option.proxy = new WebProxy(host, port);
367 | if (!string.IsNullOrEmpty(username)) option.proxy.Credentials = new NetworkCredential(username, password);
368 | return this;
369 | }
370 |
371 | #endregion
372 |
373 | #region 编码
374 |
375 | ///
376 | /// 编码
377 | ///
378 | /// 编码
379 | public HttpCore encoding(string encoding)
380 | {
381 | option.encoding = Encoding.GetEncoding(encoding);
382 | return this;
383 | }
384 |
385 | ///
386 | /// 编码
387 | ///
388 | /// 编码
389 | public HttpCore encoding(Encoding encoding)
390 | {
391 | option.encoding = encoding;
392 | return this;
393 | }
394 |
395 | ///
396 | /// 自动编码(识别html编码格式)
397 | ///
398 | public HttpCore autoencode(bool auto = true)
399 | {
400 | option.autoencode = auto;
401 | return this;
402 | }
403 |
404 | #endregion
405 |
406 | #region 重定向
407 |
408 | ///
409 | /// 重定向
410 | ///
411 | public HttpCore redirect(bool val = true)
412 | {
413 | option.redirect = val;
414 | return this;
415 | }
416 |
417 | #endregion
418 |
419 | #region 超时
420 |
421 | ///
422 | /// 超时
423 | ///
424 | /// 毫秒
425 | public HttpCore timeout(int time)
426 | {
427 | option.timeout = time;
428 | return this;
429 | }
430 |
431 | #endregion
432 |
433 | #region 缓存
434 |
435 | CacheModel? _cache = null;
436 | ///
437 | /// 设置缓存
438 | ///
439 | /// 缓存id
440 | public HttpCore cache(string id)
441 | {
442 | _cache = new CacheModel(id);
443 | return this;
444 | }
445 |
446 | ///
447 | /// 设置缓存
448 | ///
449 | /// 缓存id
450 | /// 有效期 分钟
451 | public HttpCore cache(string id, int time)
452 | {
453 | _cache = new CacheModel(id) { t = time };
454 | return this;
455 | }
456 |
457 | class CacheModel
458 | {
459 | public CacheModel(string _id)
460 | {
461 | if (Config.CacheFolder == null) throw new Exception("先配置 \"Config.CachePath\"");
462 | path = Config.CacheFolder;
463 | id = _id;
464 | file = path + _id;
465 | }
466 | public string id { get; set; }
467 | public int t = 0;
468 | public string path { get; set; }
469 | public string file { get; set; }
470 | }
471 |
472 | #endregion
473 |
474 | #region 分块
475 |
476 | ///
477 | /// 清空字节范围
478 | ///
479 | public HttpCore range()
480 | {
481 | _range = null;
482 | return this;
483 | }
484 |
485 | long[]? _range = null;
486 | ///
487 | /// 字节范围
488 | ///
489 | /// 开始发送数据的位置
490 | /// 停止发送数据的位置
491 | public HttpCore range(long from, long to)
492 | {
493 | _range = new long[] { from, to };
494 | return this;
495 | }
496 |
497 | ///
498 | /// 字节范围
499 | ///
500 | /// 开始发送数据的位置
501 | /// 停止发送数据的位置
502 | public HttpCore range(int from, int to)
503 | {
504 | _range = new long[] { from, to };
505 | return this;
506 | }
507 |
508 | #endregion
509 | }
510 |
511 | public enum HttpMethod
512 | {
513 | Get,
514 | Post,
515 | Put,
516 | Delete,
517 | Head,
518 | Patch,
519 | Options
520 | }
521 | }
--------------------------------------------------------------------------------
/examples/TDown/Form1.Designer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace TDown
3 | {
4 | partial class Form1
5 | {
6 | ///
7 | /// 必需的设计器变量。
8 | ///
9 | private System.ComponentModel.IContainer components = null;
10 |
11 | ///
12 | /// 清理所有正在使用的资源。
13 | ///
14 | /// 如果应释放托管资源,为 true;否则为 false。
15 | protected override void Dispose(bool disposing)
16 | {
17 | if (disposing && (components != null))
18 | {
19 | components.Dispose();
20 | }
21 | base.Dispose(disposing);
22 | }
23 |
24 | #region Windows 窗体设计器生成的代码
25 |
26 | ///
27 | /// 设计器支持所需的方法 - 不要修改
28 | /// 使用代码编辑器修改此方法的内容。
29 | ///
30 | private void InitializeComponent()
31 | {
32 | btn = new AntdUI.Button();
33 | txt_uri = new AntdUI.Input();
34 | textBox2 = new AntdUI.InputNumber();
35 | label1 = new AntdUI.Label();
36 | btn_suspend = new AntdUI.Button();
37 | btn_resume = new AntdUI.Button();
38 | btn_stop = new AntdUI.Button();
39 | label_prog = new AntdUI.Label();
40 | txt_max = new AntdUI.Label();
41 | txt_value = new AntdUI.Label();
42 | label_time = new AntdUI.Label();
43 | txt_speed = new AntdUI.Label();
44 | txt_state = new AntdUI.Badge();
45 | panel_url = new System.Windows.Forms.TableLayoutPanel();
46 | progress = new AntdUI.Progress();
47 | panel_info = new System.Windows.Forms.TableLayoutPanel();
48 | label_state = new AntdUI.Label();
49 | txt_prog = new AntdUI.Label();
50 | label_speed = new AntdUI.Label();
51 | txt_time = new AntdUI.Label();
52 | label_start_time = new AntdUI.Label();
53 | txt_start_time = new AntdUI.Label();
54 | txt_end_time = new AntdUI.Label();
55 | label_end_time = new AntdUI.Label();
56 | label_max = new AntdUI.Label();
57 | label_value = new AntdUI.Label();
58 | panel_btns = new System.Windows.Forms.TableLayoutPanel();
59 | panel_url.SuspendLayout();
60 | progress.SuspendLayout();
61 | panel_info.SuspendLayout();
62 | panel_btns.SuspendLayout();
63 | SuspendLayout();
64 | //
65 | // btn
66 | //
67 | btn.Dock = System.Windows.Forms.DockStyle.Fill;
68 | btn.Location = new System.Drawing.Point(739, 3);
69 | btn.Name = "btn";
70 | btn.Size = new System.Drawing.Size(80, 38);
71 | btn.TabIndex = 0;
72 | btn.Text = "下载";
73 | btn.Type = AntdUI.TTypeMini.Primary;
74 | btn.Click += btn_Click;
75 | //
76 | // txt_uri
77 | //
78 | txt_uri.BackColor = System.Drawing.Color.Transparent;
79 | txt_uri.BorderWidth = 0F;
80 | txt_uri.Dock = System.Windows.Forms.DockStyle.Fill;
81 | txt_uri.ForeColor = System.Drawing.Color.White;
82 | txt_uri.Location = new System.Drawing.Point(0, 0);
83 | txt_uri.Name = "txt_uri";
84 | txt_uri.Size = new System.Drawing.Size(730, 38);
85 | txt_uri.TabIndex = 1;
86 | txt_uri.Text = "https://codeload.github.com/EVA-SS/HttpLib/zip/refs/heads/main";
87 | //
88 | // textBox2
89 | //
90 | textBox2.Dock = System.Windows.Forms.DockStyle.Fill;
91 | textBox2.Location = new System.Drawing.Point(103, 3);
92 | textBox2.Name = "textBox2";
93 | textBox2.Size = new System.Drawing.Size(476, 38);
94 | textBox2.TabIndex = 2;
95 | textBox2.Text = "2";
96 | textBox2.Value = new decimal(new int[] { 2, 0, 0, 0 });
97 | //
98 | // label1
99 | //
100 | label1.Dock = System.Windows.Forms.DockStyle.Fill;
101 | label1.Location = new System.Drawing.Point(0, 0);
102 | label1.Margin = new System.Windows.Forms.Padding(0);
103 | label1.Name = "label1";
104 | label1.Size = new System.Drawing.Size(100, 44);
105 | label1.TabIndex = 0;
106 | label1.Text = "核心数:";
107 | label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
108 | //
109 | // btn_suspend
110 | //
111 | btn_suspend.Dock = System.Windows.Forms.DockStyle.Fill;
112 | btn_suspend.Enabled = false;
113 | btn_suspend.Location = new System.Drawing.Point(585, 3);
114 | btn_suspend.Name = "btn_suspend";
115 | btn_suspend.Size = new System.Drawing.Size(74, 38);
116 | btn_suspend.TabIndex = 3;
117 | btn_suspend.Text = "暂停";
118 | btn_suspend.Type = AntdUI.TTypeMini.Warn;
119 | btn_suspend.Click += btn_suspend_Click;
120 | //
121 | // btn_resume
122 | //
123 | btn_resume.Dock = System.Windows.Forms.DockStyle.Fill;
124 | btn_resume.Enabled = false;
125 | btn_resume.Location = new System.Drawing.Point(665, 3);
126 | btn_resume.Name = "btn_resume";
127 | btn_resume.Size = new System.Drawing.Size(74, 38);
128 | btn_resume.TabIndex = 3;
129 | btn_resume.Text = "继续";
130 | btn_resume.Type = AntdUI.TTypeMini.Success;
131 | btn_resume.Click += btn_resume_Click;
132 | //
133 | // btn_stop
134 | //
135 | btn_stop.Dock = System.Windows.Forms.DockStyle.Fill;
136 | btn_stop.Enabled = false;
137 | btn_stop.Location = new System.Drawing.Point(745, 3);
138 | btn_stop.Name = "btn_stop";
139 | btn_stop.Size = new System.Drawing.Size(74, 38);
140 | btn_stop.TabIndex = 3;
141 | btn_stop.Text = "终止";
142 | btn_stop.Type = AntdUI.TTypeMini.Error;
143 | btn_stop.Click += btn_stop_Click;
144 | //
145 | // label_prog
146 | //
147 | label_prog.Dock = System.Windows.Forms.DockStyle.Fill;
148 | label_prog.Location = new System.Drawing.Point(411, 0);
149 | label_prog.Margin = new System.Windows.Forms.Padding(0);
150 | label_prog.Name = "label_prog";
151 | label_prog.Size = new System.Drawing.Size(140, 42);
152 | label_prog.TabIndex = 0;
153 | label_prog.Text = "下载进度:";
154 | label_prog.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
155 | //
156 | // txt_max
157 | //
158 | txt_max.Dock = System.Windows.Forms.DockStyle.Fill;
159 | txt_max.Location = new System.Drawing.Point(140, 126);
160 | txt_max.Margin = new System.Windows.Forms.Padding(0);
161 | txt_max.Name = "txt_max";
162 | txt_max.Size = new System.Drawing.Size(271, 43);
163 | txt_max.TabIndex = 0;
164 | //
165 | // txt_value
166 | //
167 | txt_value.Dock = System.Windows.Forms.DockStyle.Fill;
168 | txt_value.Location = new System.Drawing.Point(551, 126);
169 | txt_value.Margin = new System.Windows.Forms.Padding(0);
170 | txt_value.Name = "txt_value";
171 | txt_value.Size = new System.Drawing.Size(271, 43);
172 | txt_value.TabIndex = 0;
173 | //
174 | // label_time
175 | //
176 | label_time.Dock = System.Windows.Forms.DockStyle.Fill;
177 | label_time.Location = new System.Drawing.Point(411, 42);
178 | label_time.Margin = new System.Windows.Forms.Padding(0);
179 | label_time.Name = "label_time";
180 | label_time.Size = new System.Drawing.Size(140, 42);
181 | label_time.TabIndex = 0;
182 | label_time.Text = "剩余时间:";
183 | label_time.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
184 | //
185 | // txt_speed
186 | //
187 | txt_speed.Dock = System.Windows.Forms.DockStyle.Fill;
188 | txt_speed.Location = new System.Drawing.Point(140, 42);
189 | txt_speed.Margin = new System.Windows.Forms.Padding(0);
190 | txt_speed.Name = "txt_speed";
191 | txt_speed.Size = new System.Drawing.Size(271, 42);
192 | txt_speed.TabIndex = 0;
193 | //
194 | // txt_state
195 | //
196 | txt_state.Dock = System.Windows.Forms.DockStyle.Fill;
197 | txt_state.Location = new System.Drawing.Point(140, 0);
198 | txt_state.Margin = new System.Windows.Forms.Padding(0);
199 | txt_state.Name = "txt_state";
200 | txt_state.Size = new System.Drawing.Size(271, 42);
201 | txt_state.TabIndex = 0;
202 | txt_state.Text = "待下载";
203 | //
204 | // panel_url
205 | //
206 | panel_url.ColumnCount = 2;
207 | panel_url.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
208 | panel_url.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 86F));
209 | panel_url.Controls.Add(progress, 0, 0);
210 | panel_url.Controls.Add(btn, 1, 0);
211 | panel_url.Dock = System.Windows.Forms.DockStyle.Top;
212 | panel_url.Location = new System.Drawing.Point(0, 0);
213 | panel_url.Margin = new System.Windows.Forms.Padding(4);
214 | panel_url.Name = "panel_url";
215 | panel_url.RowCount = 1;
216 | panel_url.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
217 | panel_url.Size = new System.Drawing.Size(822, 44);
218 | panel_url.TabIndex = 0;
219 | //
220 | // progress
221 | //
222 | progress.Back = System.Drawing.Color.Gray;
223 | progress.Controls.Add(txt_uri);
224 | progress.Dock = System.Windows.Forms.DockStyle.Fill;
225 | progress.Location = new System.Drawing.Point(3, 3);
226 | progress.Name = "progress";
227 | progress.Shape = AntdUI.TShape.Default;
228 | progress.Size = new System.Drawing.Size(730, 38);
229 | progress.TabIndex = 0;
230 | //
231 | // panel_info
232 | //
233 | panel_info.ColumnCount = 4;
234 | panel_info.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 140F));
235 | panel_info.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
236 | panel_info.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 140F));
237 | panel_info.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
238 | panel_info.Controls.Add(txt_state, 1, 0);
239 | panel_info.Controls.Add(label_state, 0, 0);
240 | panel_info.Controls.Add(label_prog, 2, 0);
241 | panel_info.Controls.Add(txt_prog, 3, 0);
242 | panel_info.Controls.Add(label_speed, 0, 1);
243 | panel_info.Controls.Add(txt_speed, 1, 1);
244 | panel_info.Controls.Add(label_time, 2, 1);
245 | panel_info.Controls.Add(txt_time, 3, 1);
246 | panel_info.Controls.Add(label_start_time, 0, 2);
247 | panel_info.Controls.Add(txt_start_time, 1, 2);
248 | panel_info.Controls.Add(txt_end_time, 3, 2);
249 | panel_info.Controls.Add(label_end_time, 2, 2);
250 | panel_info.Controls.Add(label_max, 0, 3);
251 | panel_info.Controls.Add(txt_max, 1, 3);
252 | panel_info.Controls.Add(label_value, 2, 3);
253 | panel_info.Controls.Add(txt_value, 3, 3);
254 | panel_info.Dock = System.Windows.Forms.DockStyle.Fill;
255 | panel_info.Location = new System.Drawing.Point(0, 88);
256 | panel_info.Name = "panel_info";
257 | panel_info.RowCount = 4;
258 | panel_info.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
259 | panel_info.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
260 | panel_info.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
261 | panel_info.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
262 | panel_info.Size = new System.Drawing.Size(822, 169);
263 | panel_info.TabIndex = 0;
264 | //
265 | // label_state
266 | //
267 | label_state.Dock = System.Windows.Forms.DockStyle.Fill;
268 | label_state.Location = new System.Drawing.Point(0, 0);
269 | label_state.Margin = new System.Windows.Forms.Padding(0);
270 | label_state.Name = "label_state";
271 | label_state.Size = new System.Drawing.Size(140, 42);
272 | label_state.TabIndex = 0;
273 | label_state.Text = "状态:";
274 | label_state.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
275 | //
276 | // txt_prog
277 | //
278 | txt_prog.Dock = System.Windows.Forms.DockStyle.Fill;
279 | txt_prog.Font = new System.Drawing.Font("Microsoft YaHei UI", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
280 | txt_prog.Location = new System.Drawing.Point(551, 0);
281 | txt_prog.Margin = new System.Windows.Forms.Padding(0);
282 | txt_prog.Name = "txt_prog";
283 | txt_prog.Size = new System.Drawing.Size(271, 42);
284 | txt_prog.TabIndex = 0;
285 | //
286 | // label_speed
287 | //
288 | label_speed.Dock = System.Windows.Forms.DockStyle.Fill;
289 | label_speed.Location = new System.Drawing.Point(0, 42);
290 | label_speed.Margin = new System.Windows.Forms.Padding(0);
291 | label_speed.Name = "label_speed";
292 | label_speed.Size = new System.Drawing.Size(140, 42);
293 | label_speed.TabIndex = 0;
294 | label_speed.Text = "下载速度:";
295 | label_speed.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
296 | //
297 | // txt_time
298 | //
299 | txt_time.Dock = System.Windows.Forms.DockStyle.Fill;
300 | txt_time.Location = new System.Drawing.Point(551, 42);
301 | txt_time.Margin = new System.Windows.Forms.Padding(0);
302 | txt_time.Name = "txt_time";
303 | txt_time.Size = new System.Drawing.Size(271, 42);
304 | txt_time.TabIndex = 0;
305 | //
306 | // label_start_time
307 | //
308 | label_start_time.Dock = System.Windows.Forms.DockStyle.Fill;
309 | label_start_time.Location = new System.Drawing.Point(0, 84);
310 | label_start_time.Margin = new System.Windows.Forms.Padding(0);
311 | label_start_time.Name = "label_start_time";
312 | label_start_time.Size = new System.Drawing.Size(140, 42);
313 | label_start_time.TabIndex = 0;
314 | label_start_time.Text = "开始时间:";
315 | label_start_time.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
316 | //
317 | // txt_start_time
318 | //
319 | txt_start_time.Dock = System.Windows.Forms.DockStyle.Fill;
320 | txt_start_time.Location = new System.Drawing.Point(140, 84);
321 | txt_start_time.Margin = new System.Windows.Forms.Padding(0);
322 | txt_start_time.Name = "txt_start_time";
323 | txt_start_time.Size = new System.Drawing.Size(271, 42);
324 | txt_start_time.TabIndex = 0;
325 | //
326 | // txt_end_time
327 | //
328 | txt_end_time.Dock = System.Windows.Forms.DockStyle.Fill;
329 | txt_end_time.Location = new System.Drawing.Point(551, 84);
330 | txt_end_time.Margin = new System.Windows.Forms.Padding(0);
331 | txt_end_time.Name = "txt_end_time";
332 | txt_end_time.Size = new System.Drawing.Size(271, 42);
333 | txt_end_time.TabIndex = 0;
334 | //
335 | // label_end_time
336 | //
337 | label_end_time.Dock = System.Windows.Forms.DockStyle.Fill;
338 | label_end_time.Location = new System.Drawing.Point(411, 84);
339 | label_end_time.Margin = new System.Windows.Forms.Padding(0);
340 | label_end_time.Name = "label_end_time";
341 | label_end_time.Size = new System.Drawing.Size(140, 42);
342 | label_end_time.TabIndex = 0;
343 | label_end_time.Text = "完成时间:";
344 | label_end_time.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
345 | //
346 | // label_max
347 | //
348 | label_max.Dock = System.Windows.Forms.DockStyle.Fill;
349 | label_max.Location = new System.Drawing.Point(0, 126);
350 | label_max.Margin = new System.Windows.Forms.Padding(0);
351 | label_max.Name = "label_max";
352 | label_max.Size = new System.Drawing.Size(140, 43);
353 | label_max.TabIndex = 0;
354 | label_max.Text = "Max:";
355 | label_max.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
356 | //
357 | // label_value
358 | //
359 | label_value.Dock = System.Windows.Forms.DockStyle.Fill;
360 | label_value.Location = new System.Drawing.Point(411, 126);
361 | label_value.Margin = new System.Windows.Forms.Padding(0);
362 | label_value.Name = "label_value";
363 | label_value.Size = new System.Drawing.Size(140, 43);
364 | label_value.TabIndex = 0;
365 | label_value.Text = "Value:";
366 | label_value.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
367 | //
368 | // panel_btns
369 | //
370 | panel_btns.ColumnCount = 5;
371 | panel_btns.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F));
372 | panel_btns.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
373 | panel_btns.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F));
374 | panel_btns.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F));
375 | panel_btns.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F));
376 | panel_btns.Controls.Add(textBox2, 1, 0);
377 | panel_btns.Controls.Add(btn_stop, 4, 0);
378 | panel_btns.Controls.Add(label1, 0, 0);
379 | panel_btns.Controls.Add(btn_resume, 3, 0);
380 | panel_btns.Controls.Add(btn_suspend, 2, 0);
381 | panel_btns.Dock = System.Windows.Forms.DockStyle.Top;
382 | panel_btns.Location = new System.Drawing.Point(0, 44);
383 | panel_btns.Name = "panel_btns";
384 | panel_btns.RowCount = 1;
385 | panel_btns.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
386 | panel_btns.Size = new System.Drawing.Size(822, 44);
387 | panel_btns.TabIndex = 1;
388 | //
389 | // Form1
390 | //
391 | AutoScaleDimensions = new System.Drawing.SizeF(10F, 21F);
392 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
393 | ClientSize = new System.Drawing.Size(822, 257);
394 | Controls.Add(panel_info);
395 | Controls.Add(panel_btns);
396 | Controls.Add(panel_url);
397 | Font = new System.Drawing.Font("Microsoft YaHei UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
398 | Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
399 | MinimumSize = new System.Drawing.Size(838, 296);
400 | Name = "Form1";
401 | StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
402 | Text = "下载文件DEMO";
403 | panel_url.ResumeLayout(false);
404 | progress.ResumeLayout(false);
405 | panel_info.ResumeLayout(false);
406 | panel_btns.ResumeLayout(false);
407 | ResumeLayout(false);
408 | }
409 |
410 | #endregion
411 |
412 | private AntdUI.Button btn;
413 | private AntdUI.Input txt_uri;
414 | private AntdUI.InputNumber textBox2;
415 | private AntdUI.Label label1;
416 | private AntdUI.Button btn_suspend;
417 | private AntdUI.Button btn_resume;
418 | private AntdUI.Button btn_stop;
419 | private AntdUI.Label label_prog;
420 | private AntdUI.Label txt_max;
421 | private AntdUI.Label txt_value;
422 | private AntdUI.Label label_time;
423 | private AntdUI.Label txt_speed;
424 | private AntdUI.Badge txt_state;
425 | private System.Windows.Forms.TableLayoutPanel panel_url;
426 | private AntdUI.Progress progress;
427 | private System.Windows.Forms.TableLayoutPanel panel_info;
428 | private AntdUI.Label label_state;
429 | private AntdUI.Label txt_prog;
430 | private AntdUI.Label txt_time;
431 | private AntdUI.Label label_speed;
432 | private AntdUI.Label label_max;
433 | private AntdUI.Label label_value;
434 | private System.Windows.Forms.TableLayoutPanel panel_btns;
435 | private AntdUI.Label label_start_time;
436 | private AntdUI.Label txt_start_time;
437 | private AntdUI.Label txt_end_time;
438 | private AntdUI.Label label_end_time;
439 | }
440 | }
441 |
--------------------------------------------------------------------------------
/src/Core/MimeMapping.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace HttpLib
6 | {
7 | public static class MimeMapping
8 | {
9 | abstract class MimeMappingDictionaryBase
10 | {
11 | readonly Dictionary _mappings = new Dictionary(StringComparer.OrdinalIgnoreCase);
12 |
13 | static readonly char[] _pathSeparatorChars = new char[3] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, Path.VolumeSeparatorChar };
14 |
15 | bool _isInitialized;
16 |
17 | protected void AddMapping(string fileExtension, string mimeType) => _mappings.Add(fileExtension, mimeType);
18 |
19 | void AddWildcardIfNotPresent()
20 | {
21 | if (!_mappings.ContainsKey(".*")) AddMapping(".*", "application/octet-stream");
22 | }
23 |
24 | void EnsureMapping()
25 | {
26 | if (!_isInitialized)
27 | {
28 | lock (this)
29 | {
30 | if (!_isInitialized)
31 | {
32 | PopulateMappings();
33 | AddWildcardIfNotPresent();
34 | _isInitialized = true;
35 | }
36 | }
37 | }
38 | }
39 |
40 | protected abstract void PopulateMappings();
41 |
42 | private static string GetFileName(string path)
43 | {
44 | int num = path.LastIndexOfAny(_pathSeparatorChars);
45 | if (num < 0) return path;
46 | return path.Substring(num);
47 | }
48 |
49 | public string GetMimeMapping(string fileName)
50 | {
51 | EnsureMapping();
52 | fileName = GetFileName(fileName);
53 | for (int i = 0; i < fileName.Length; i++)
54 | {
55 | if (fileName[i] == '.' && _mappings.TryGetValue(fileName.Substring(i), out var result) && result != null) return result;
56 | }
57 | return _mappings[".*"];
58 | }
59 | }
60 |
61 | private sealed class MimeMappingDictionaryClassic : MimeMappingDictionaryBase
62 | {
63 | protected override void PopulateMappings()
64 | {
65 | AddMapping(".323", "text/h323");
66 | AddMapping(".aaf", "application/octet-stream");
67 | AddMapping(".aca", "application/octet-stream");
68 | AddMapping(".accdb", "application/msaccess");
69 | AddMapping(".accde", "application/msaccess");
70 | AddMapping(".accdt", "application/msaccess");
71 | AddMapping(".acx", "application/internet-property-stream");
72 | AddMapping(".afm", "application/octet-stream");
73 | AddMapping(".ai", "application/postscript");
74 | AddMapping(".aif", "audio/x-aiff");
75 | AddMapping(".aifc", "audio/aiff");
76 | AddMapping(".aiff", "audio/aiff");
77 | AddMapping(".application", "application/x-ms-application");
78 | AddMapping(".art", "image/x-jg");
79 | AddMapping(".asd", "application/octet-stream");
80 | AddMapping(".asf", "video/x-ms-asf");
81 | AddMapping(".asi", "application/octet-stream");
82 | AddMapping(".asm", "text/plain");
83 | AddMapping(".asr", "video/x-ms-asf");
84 | AddMapping(".asx", "video/x-ms-asf");
85 | AddMapping(".atom", "application/atom+xml");
86 | AddMapping(".au", "audio/basic");
87 | AddMapping(".avi", "video/x-msvideo");
88 | AddMapping(".axs", "application/olescript");
89 | AddMapping(".bas", "text/plain");
90 | AddMapping(".bcpio", "application/x-bcpio");
91 | AddMapping(".bin", "application/octet-stream");
92 | AddMapping(".bmp", "image/bmp");
93 | AddMapping(".c", "text/plain");
94 | AddMapping(".cab", "application/octet-stream");
95 | AddMapping(".calx", "application/vnd.ms-office.calx");
96 | AddMapping(".cat", "application/vnd.ms-pki.seccat");
97 | AddMapping(".cdf", "application/x-cdf");
98 | AddMapping(".chm", "application/octet-stream");
99 | AddMapping(".class", "application/x-java-applet");
100 | AddMapping(".clp", "application/x-msclip");
101 | AddMapping(".cmx", "image/x-cmx");
102 | AddMapping(".cnf", "text/plain");
103 | AddMapping(".cod", "image/cis-cod");
104 | AddMapping(".cpio", "application/x-cpio");
105 | AddMapping(".cpp", "text/plain");
106 | AddMapping(".crd", "application/x-mscardfile");
107 | AddMapping(".crl", "application/pkix-crl");
108 | AddMapping(".crt", "application/x-x509-ca-cert");
109 | AddMapping(".csh", "application/x-csh");
110 | AddMapping(".css", "text/css");
111 | AddMapping(".csv", "application/octet-stream");
112 | AddMapping(".cur", "application/octet-stream");
113 | AddMapping(".dcr", "application/x-director");
114 | AddMapping(".deploy", "application/octet-stream");
115 | AddMapping(".der", "application/x-x509-ca-cert");
116 | AddMapping(".dib", "image/bmp");
117 | AddMapping(".dir", "application/x-director");
118 | AddMapping(".disco", "text/xml");
119 | AddMapping(".dll", "application/x-msdownload");
120 | AddMapping(".dll.config", "text/xml");
121 | AddMapping(".dlm", "text/dlm");
122 | AddMapping(".doc", "application/msword");
123 | AddMapping(".docm", "application/vnd.ms-word.document.macroEnabled.12");
124 | AddMapping(".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
125 | AddMapping(".dot", "application/msword");
126 | AddMapping(".dotm", "application/vnd.ms-word.template.macroEnabled.12");
127 | AddMapping(".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template");
128 | AddMapping(".dsp", "application/octet-stream");
129 | AddMapping(".dtd", "text/xml");
130 | AddMapping(".dvi", "application/x-dvi");
131 | AddMapping(".dwf", "drawing/x-dwf");
132 | AddMapping(".dwp", "application/octet-stream");
133 | AddMapping(".dxr", "application/x-director");
134 | AddMapping(".eml", "message/rfc822");
135 | AddMapping(".emz", "application/octet-stream");
136 | AddMapping(".eot", "application/octet-stream");
137 | AddMapping(".eps", "application/postscript");
138 | AddMapping(".etx", "text/x-setext");
139 | AddMapping(".evy", "application/envoy");
140 | AddMapping(".exe", "application/octet-stream");
141 | AddMapping(".exe.config", "text/xml");
142 | AddMapping(".fdf", "application/vnd.fdf");
143 | AddMapping(".fif", "application/fractals");
144 | AddMapping(".fla", "application/octet-stream");
145 | AddMapping(".flr", "x-world/x-vrml");
146 | AddMapping(".flv", "video/x-flv");
147 | AddMapping(".gif", "image/gif");
148 | AddMapping(".gtar", "application/x-gtar");
149 | AddMapping(".gz", "application/x-gzip");
150 | AddMapping(".h", "text/plain");
151 | AddMapping(".hdf", "application/x-hdf");
152 | AddMapping(".hdml", "text/x-hdml");
153 | AddMapping(".hhc", "application/x-oleobject");
154 | AddMapping(".hhk", "application/octet-stream");
155 | AddMapping(".hhp", "application/octet-stream");
156 | AddMapping(".hlp", "application/winhlp");
157 | AddMapping(".hqx", "application/mac-binhex40");
158 | AddMapping(".hta", "application/hta");
159 | AddMapping(".htc", "text/x-component");
160 | AddMapping(".htm", "text/html");
161 | AddMapping(".html", "text/html");
162 | AddMapping(".htt", "text/webviewhtml");
163 | AddMapping(".hxt", "text/html");
164 | AddMapping(".ico", "image/x-icon");
165 | AddMapping(".ics", "application/octet-stream");
166 | AddMapping(".ief", "image/ief");
167 | AddMapping(".iii", "application/x-iphone");
168 | AddMapping(".inf", "application/octet-stream");
169 | AddMapping(".ins", "application/x-internet-signup");
170 | AddMapping(".isp", "application/x-internet-signup");
171 | AddMapping(".IVF", "video/x-ivf");
172 | AddMapping(".jar", "application/java-archive");
173 | AddMapping(".java", "application/octet-stream");
174 | AddMapping(".jck", "application/liquidmotion");
175 | AddMapping(".jcz", "application/liquidmotion");
176 | AddMapping(".jfif", "image/pjpeg");
177 | AddMapping(".jpb", "application/octet-stream");
178 | AddMapping(".jpe", "image/jpeg");
179 | AddMapping(".jpeg", "image/jpeg");
180 | AddMapping(".jpg", "image/jpeg");
181 | AddMapping(".js", "application/x-javascript");
182 | AddMapping(".jsx", "text/jscript");
183 | AddMapping(".latex", "application/x-latex");
184 | AddMapping(".lit", "application/x-ms-reader");
185 | AddMapping(".lpk", "application/octet-stream");
186 | AddMapping(".lsf", "video/x-la-asf");
187 | AddMapping(".lsx", "video/x-la-asf");
188 | AddMapping(".lzh", "application/octet-stream");
189 | AddMapping(".m13", "application/x-msmediaview");
190 | AddMapping(".m14", "application/x-msmediaview");
191 | AddMapping(".m1v", "video/mpeg");
192 | AddMapping(".m3u", "audio/x-mpegurl");
193 | AddMapping(".man", "application/x-troff-man");
194 | AddMapping(".manifest", "application/x-ms-manifest");
195 | AddMapping(".map", "text/plain");
196 | AddMapping(".mdb", "application/x-msaccess");
197 | AddMapping(".mdp", "application/octet-stream");
198 | AddMapping(".me", "application/x-troff-me");
199 | AddMapping(".mht", "message/rfc822");
200 | AddMapping(".mhtml", "message/rfc822");
201 | AddMapping(".mid", "audio/mid");
202 | AddMapping(".midi", "audio/mid");
203 | AddMapping(".mix", "application/octet-stream");
204 | AddMapping(".mmf", "application/x-smaf");
205 | AddMapping(".mno", "text/xml");
206 | AddMapping(".mny", "application/x-msmoney");
207 | AddMapping(".mov", "video/quicktime");
208 | AddMapping(".movie", "video/x-sgi-movie");
209 | AddMapping(".mp2", "video/mpeg");
210 | AddMapping(".mp3", "audio/mpeg");
211 | AddMapping(".mpa", "video/mpeg");
212 | AddMapping(".mpe", "video/mpeg");
213 | AddMapping(".mpeg", "video/mpeg");
214 | AddMapping(".mpg", "video/mpeg");
215 | AddMapping(".mpp", "application/vnd.ms-project");
216 | AddMapping(".mpv2", "video/mpeg");
217 | AddMapping(".ms", "application/x-troff-ms");
218 | AddMapping(".msi", "application/octet-stream");
219 | AddMapping(".mso", "application/octet-stream");
220 | AddMapping(".mvb", "application/x-msmediaview");
221 | AddMapping(".mvc", "application/x-miva-compiled");
222 | AddMapping(".nc", "application/x-netcdf");
223 | AddMapping(".nsc", "video/x-ms-asf");
224 | AddMapping(".nws", "message/rfc822");
225 | AddMapping(".ocx", "application/octet-stream");
226 | AddMapping(".oda", "application/oda");
227 | AddMapping(".odc", "text/x-ms-odc");
228 | AddMapping(".ods", "application/oleobject");
229 | AddMapping(".one", "application/onenote");
230 | AddMapping(".onea", "application/onenote");
231 | AddMapping(".onetoc", "application/onenote");
232 | AddMapping(".onetoc2", "application/onenote");
233 | AddMapping(".onetmp", "application/onenote");
234 | AddMapping(".onepkg", "application/onenote");
235 | AddMapping(".osdx", "application/opensearchdescription+xml");
236 | AddMapping(".p10", "application/pkcs10");
237 | AddMapping(".p12", "application/x-pkcs12");
238 | AddMapping(".p7b", "application/x-pkcs7-certificates");
239 | AddMapping(".p7c", "application/pkcs7-mime");
240 | AddMapping(".p7m", "application/pkcs7-mime");
241 | AddMapping(".p7r", "application/x-pkcs7-certreqresp");
242 | AddMapping(".p7s", "application/pkcs7-signature");
243 | AddMapping(".pbm", "image/x-portable-bitmap");
244 | AddMapping(".pcx", "application/octet-stream");
245 | AddMapping(".pcz", "application/octet-stream");
246 | AddMapping(".pdf", "application/pdf");
247 | AddMapping(".pfb", "application/octet-stream");
248 | AddMapping(".pfm", "application/octet-stream");
249 | AddMapping(".pfx", "application/x-pkcs12");
250 | AddMapping(".pgm", "image/x-portable-graymap");
251 | AddMapping(".pko", "application/vnd.ms-pki.pko");
252 | AddMapping(".pma", "application/x-perfmon");
253 | AddMapping(".pmc", "application/x-perfmon");
254 | AddMapping(".pml", "application/x-perfmon");
255 | AddMapping(".pmr", "application/x-perfmon");
256 | AddMapping(".pmw", "application/x-perfmon");
257 | AddMapping(".png", "image/png");
258 | AddMapping(".pnm", "image/x-portable-anymap");
259 | AddMapping(".pnz", "image/png");
260 | AddMapping(".pot", "application/vnd.ms-powerpoint");
261 | AddMapping(".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12");
262 | AddMapping(".potx", "application/vnd.openxmlformats-officedocument.presentationml.template");
263 | AddMapping(".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12");
264 | AddMapping(".ppm", "image/x-portable-pixmap");
265 | AddMapping(".pps", "application/vnd.ms-powerpoint");
266 | AddMapping(".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12");
267 | AddMapping(".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow");
268 | AddMapping(".ppt", "application/vnd.ms-powerpoint");
269 | AddMapping(".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12");
270 | AddMapping(".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
271 | AddMapping(".prf", "application/pics-rules");
272 | AddMapping(".prm", "application/octet-stream");
273 | AddMapping(".prx", "application/octet-stream");
274 | AddMapping(".ps", "application/postscript");
275 | AddMapping(".psd", "application/octet-stream");
276 | AddMapping(".psm", "application/octet-stream");
277 | AddMapping(".psp", "application/octet-stream");
278 | AddMapping(".pub", "application/x-mspublisher");
279 | AddMapping(".qt", "video/quicktime");
280 | AddMapping(".qtl", "application/x-quicktimeplayer");
281 | AddMapping(".qxd", "application/octet-stream");
282 | AddMapping(".ra", "audio/x-pn-realaudio");
283 | AddMapping(".ram", "audio/x-pn-realaudio");
284 | AddMapping(".rar", "application/octet-stream");
285 | AddMapping(".ras", "image/x-cmu-raster");
286 | AddMapping(".rf", "image/vnd.rn-realflash");
287 | AddMapping(".rgb", "image/x-rgb");
288 | AddMapping(".rm", "application/vnd.rn-realmedia");
289 | AddMapping(".rmi", "audio/mid");
290 | AddMapping(".roff", "application/x-troff");
291 | AddMapping(".rpm", "audio/x-pn-realaudio-plugin");
292 | AddMapping(".rtf", "application/rtf");
293 | AddMapping(".rtx", "text/richtext");
294 | AddMapping(".scd", "application/x-msschedule");
295 | AddMapping(".sct", "text/scriptlet");
296 | AddMapping(".sea", "application/octet-stream");
297 | AddMapping(".setpay", "application/set-payment-initiation");
298 | AddMapping(".setreg", "application/set-registration-initiation");
299 | AddMapping(".sgml", "text/sgml");
300 | AddMapping(".sh", "application/x-sh");
301 | AddMapping(".shar", "application/x-shar");
302 | AddMapping(".sit", "application/x-stuffit");
303 | AddMapping(".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12");
304 | AddMapping(".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide");
305 | AddMapping(".smd", "audio/x-smd");
306 | AddMapping(".smi", "application/octet-stream");
307 | AddMapping(".smx", "audio/x-smd");
308 | AddMapping(".smz", "audio/x-smd");
309 | AddMapping(".snd", "audio/basic");
310 | AddMapping(".snp", "application/octet-stream");
311 | AddMapping(".spc", "application/x-pkcs7-certificates");
312 | AddMapping(".spl", "application/futuresplash");
313 | AddMapping(".src", "application/x-wais-source");
314 | AddMapping(".ssm", "application/streamingmedia");
315 | AddMapping(".sst", "application/vnd.ms-pki.certstore");
316 | AddMapping(".stl", "application/vnd.ms-pki.stl");
317 | AddMapping(".sv4cpio", "application/x-sv4cpio");
318 | AddMapping(".sv4crc", "application/x-sv4crc");
319 | AddMapping(".swf", "application/x-shockwave-flash");
320 | AddMapping(".t", "application/x-troff");
321 | AddMapping(".tar", "application/x-tar");
322 | AddMapping(".tcl", "application/x-tcl");
323 | AddMapping(".tex", "application/x-tex");
324 | AddMapping(".texi", "application/x-texinfo");
325 | AddMapping(".texinfo", "application/x-texinfo");
326 | AddMapping(".tgz", "application/x-compressed");
327 | AddMapping(".thmx", "application/vnd.ms-officetheme");
328 | AddMapping(".thn", "application/octet-stream");
329 | AddMapping(".tif", "image/tiff");
330 | AddMapping(".tiff", "image/tiff");
331 | AddMapping(".toc", "application/octet-stream");
332 | AddMapping(".tr", "application/x-troff");
333 | AddMapping(".trm", "application/x-msterminal");
334 | AddMapping(".tsv", "text/tab-separated-values");
335 | AddMapping(".ttf", "application/octet-stream");
336 | AddMapping(".txt", "text/plain");
337 | AddMapping(".u32", "application/octet-stream");
338 | AddMapping(".uls", "text/iuls");
339 | AddMapping(".ustar", "application/x-ustar");
340 | AddMapping(".vbs", "text/vbscript");
341 | AddMapping(".vcf", "text/x-vcard");
342 | AddMapping(".vcs", "text/plain");
343 | AddMapping(".vdx", "application/vnd.ms-visio.viewer");
344 | AddMapping(".vml", "text/xml");
345 | AddMapping(".vsd", "application/vnd.visio");
346 | AddMapping(".vss", "application/vnd.visio");
347 | AddMapping(".vst", "application/vnd.visio");
348 | AddMapping(".vsto", "application/x-ms-vsto");
349 | AddMapping(".vsw", "application/vnd.visio");
350 | AddMapping(".vsx", "application/vnd.visio");
351 | AddMapping(".vtx", "application/vnd.visio");
352 | AddMapping(".wav", "audio/wav");
353 | AddMapping(".wax", "audio/x-ms-wax");
354 | AddMapping(".wbmp", "image/vnd.wap.wbmp");
355 | AddMapping(".wcm", "application/vnd.ms-works");
356 | AddMapping(".wdb", "application/vnd.ms-works");
357 | AddMapping(".wks", "application/vnd.ms-works");
358 | AddMapping(".wm", "video/x-ms-wm");
359 | AddMapping(".wma", "audio/x-ms-wma");
360 | AddMapping(".wmd", "application/x-ms-wmd");
361 | AddMapping(".wmf", "application/x-msmetafile");
362 | AddMapping(".wml", "text/vnd.wap.wml");
363 | AddMapping(".wmlc", "application/vnd.wap.wmlc");
364 | AddMapping(".wmls", "text/vnd.wap.wmlscript");
365 | AddMapping(".wmlsc", "application/vnd.wap.wmlscriptc");
366 | AddMapping(".wmp", "video/x-ms-wmp");
367 | AddMapping(".wmv", "video/x-ms-wmv");
368 | AddMapping(".wmx", "video/x-ms-wmx");
369 | AddMapping(".wmz", "application/x-ms-wmz");
370 | AddMapping(".wps", "application/vnd.ms-works");
371 | AddMapping(".wri", "application/x-mswrite");
372 | AddMapping(".wrl", "x-world/x-vrml");
373 | AddMapping(".wrz", "x-world/x-vrml");
374 | AddMapping(".wsdl", "text/xml");
375 | AddMapping(".wvx", "video/x-ms-wvx");
376 | AddMapping(".x", "application/directx");
377 | AddMapping(".xaf", "x-world/x-vrml");
378 | AddMapping(".xaml", "application/xaml+xml");
379 | AddMapping(".xap", "application/x-silverlight-app");
380 | AddMapping(".xbap", "application/x-ms-xbap");
381 | AddMapping(".xbm", "image/x-xbitmap");
382 | AddMapping(".xdr", "text/plain");
383 | AddMapping(".xla", "application/vnd.ms-excel");
384 | AddMapping(".xlam", "application/vnd.ms-excel.addin.macroEnabled.12");
385 | AddMapping(".xlc", "application/vnd.ms-excel");
386 | AddMapping(".xlm", "application/vnd.ms-excel");
387 | AddMapping(".xls", "application/vnd.ms-excel");
388 | AddMapping(".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12");
389 | AddMapping(".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12");
390 | AddMapping(".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
391 | AddMapping(".xlt", "application/vnd.ms-excel");
392 | AddMapping(".xltm", "application/vnd.ms-excel.template.macroEnabled.12");
393 | AddMapping(".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template");
394 | AddMapping(".xlw", "application/vnd.ms-excel");
395 | AddMapping(".xml", "text/xml");
396 | AddMapping(".xof", "x-world/x-vrml");
397 | AddMapping(".xpm", "image/x-xpixmap");
398 | AddMapping(".xps", "application/vnd.ms-xpsdocument");
399 | AddMapping(".xsd", "text/xml");
400 | AddMapping(".xsf", "text/xml");
401 | AddMapping(".xsl", "text/xml");
402 | AddMapping(".xslt", "text/xml");
403 | AddMapping(".xsn", "application/octet-stream");
404 | AddMapping(".xtp", "application/octet-stream");
405 | AddMapping(".xwd", "image/x-xwindowdump");
406 | AddMapping(".z", "application/x-compress");
407 | AddMapping(".zip", "application/x-zip-compressed");
408 | }
409 | }
410 | static readonly MimeMappingDictionaryBase _mappingDictionary = new MimeMappingDictionaryClassic();
411 |
412 | public static string GetMimeMapping(string fileName) => _mappingDictionary.GetMimeMapping(fileName);
413 | }
414 | }
--------------------------------------------------------------------------------
/src/HttpCore.Request.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.IO.Compression;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Text;
8 | using System.Text.RegularExpressions;
9 |
10 | namespace HttpLib
11 | {
12 | partial class HttpCore
13 | {
14 | ///
15 | /// 请求(不下载流)
16 | ///
17 | public ResultResponse requestNone()
18 | {
19 | var val = Go(0);
20 | return val.web;
21 | }
22 |
23 | ///
24 | /// 下载文件
25 | ///
26 | /// 保存目录
27 | /// 保存文件名称(为空自动获取)
28 | /// 返回保存文件路径,为空下载失败
29 | public string? download(string savePath, string? saveName = null)
30 | {
31 | var val = Go(3, savePath, saveName);
32 | return val.str;
33 | }
34 |
35 | ///
36 | /// 下载文件
37 | ///
38 | /// 保存目录
39 | /// 保存文件名称(为空自动获取)
40 | /// 返回保存文件路径,为空下载失败
41 | public string? download(out ResultResponse result, string savePath, string? saveName = null)
42 | {
43 | var val = Go(3, savePath, saveName);
44 | result = val.web;
45 | return val.str;
46 | }
47 |
48 | ///
49 | /// 请求(返回字节)
50 | ///
51 | /// 响应结果
52 | public byte[]? requestData(out ResultResponse result)
53 | {
54 | var val = Go(2);
55 | result = val.web;
56 | return val.data;
57 | }
58 |
59 | ///
60 | /// 请求(返回字节)
61 | ///
62 | public byte[]? requestData()
63 | {
64 | var val = Go(2);
65 | return val.data;
66 | }
67 |
68 | ///
69 | /// 请求(返回字符串)
70 | ///
71 | public string? request()
72 | {
73 | action_eventstream = null;
74 | var val = Go(1);
75 | return val.str;
76 | }
77 |
78 | ///
79 | /// 请求(返回字符串)
80 | ///
81 | /// 响应结果
82 | public string? request(out ResultResponse result)
83 | {
84 | action_eventstream = null;
85 | var val = Go(1);
86 | result = val.web;
87 | return val.str;
88 | }
89 |
90 | Action? action_eventstream = null;
91 | ///
92 | /// 流式请求(返回字符串)
93 | ///
94 | public string? request(Action eventstream)
95 | {
96 | action_eventstream = eventstream;
97 | var val = Go(1);
98 | return val.str;
99 | }
100 |
101 | ///
102 | /// 流式请求(返回字符串)
103 | ///
104 | /// 响应结果
105 | public string? request(Action eventstream, out ResultResponse result)
106 | {
107 | action_eventstream = eventstream;
108 | var val = Go(1);
109 | result = val.web;
110 | return val.str;
111 | }
112 |
113 | #region 对象
114 |
115 | HttpWebRequest? req;
116 | HttpWebResponse? response = null;
117 |
118 | #endregion
119 |
120 | TaskResult Go(int resultMode, string? savePath = null, string? saveName = null)
121 | {
122 | var r = GoCore(resultMode, savePath, saveName);
123 | req = null;
124 | response = null;
125 | return r;
126 | }
127 | TaskResult GoCore(int resultMode, string? savePath = null, string? saveName = null)
128 | {
129 | try
130 | {
131 | if (_cache != null)
132 | {
133 | if (File.Exists(_cache.file))
134 | {
135 | if (_cache.t > 0)
136 | {
137 | var t = File.GetCreationTime(_cache.file);
138 | var elapsedTicks = DateTime.Now.Ticks - t.Ticks;
139 | var elapsedSpan = new TimeSpan(elapsedTicks);
140 | if (elapsedSpan.TotalMinutes < _cache.t)
141 | {
142 | switch (resultMode)
143 | {
144 | case 1:
145 | return new TaskResult(new ResultResponse(option.Url)
146 | {
147 | StatusCode = 200
148 | }, File.ReadAllText(_cache.file));
149 | case 2:
150 | return new TaskResult(new ResultResponse(option.Url)
151 | {
152 | StatusCode = 200
153 | }, File.ReadAllBytes(_cache.file));
154 | }
155 | }
156 | }
157 | else
158 | {
159 | switch (resultMode)
160 | {
161 | case 1:
162 | return new TaskResult(new ResultResponse(option.Url)
163 | {
164 | StatusCode = 200
165 | }, File.ReadAllText(_cache.file));
166 | case 2:
167 | return new TaskResult(new ResultResponse(option.Url)
168 | {
169 | StatusCode = 200
170 | }, File.ReadAllBytes(_cache.file));
171 | }
172 | }
173 | }
174 | }
175 | abort();
176 | req = CreateRequest();
177 | action_Request?.Invoke(req);
178 | using (response = (HttpWebResponse)req.GetResponse())
179 | {
180 | var response_max = response.ContentLength;
181 | Max = response_max;
182 | _responseProgresMax?.Invoke(response_max);
183 |
184 | var _web = new ResultResponse(response);
185 | if (action_before != null && !action_before(response, _web)) return new TaskResult(_web);
186 |
187 | switch (resultMode)
188 | {
189 | case 3:
190 | using (var stream = response.GetResponseStream())
191 | {
192 | DownStream(response_max, _web, stream, out var outfile, savePath, saveName);
193 | return new TaskResult(_web, outfile);
194 | }
195 | case 1:
196 | if (action_eventstream == null)
197 | {
198 | using (var stream = response.GetResponseStream())
199 | {
200 | var data = DownStream(response_max, _web, stream, out _);
201 | if (data == null) return new TaskResult(_web);
202 | else
203 | {
204 | var encodings = option.encoding;
205 | if (encodings == null || option.autoencode) encodings = GetEncoding(response, data);
206 |
207 | var result = encodings.GetString(data);
208 | if (_cache != null)
209 | {
210 | _cache.path.CreateDirectory();
211 | File.WriteAllText(_cache.file, result);
212 | }
213 | return new TaskResult(_web, result);
214 | }
215 | }
216 | }
217 | else
218 | {
219 | using (var reader = new StreamReader(response.GetResponseStream()))
220 | {
221 | while (!reader.EndOfStream) action_eventstream(reader.ReadLine());
222 | return new TaskResult(_web);
223 | }
224 | }
225 | case 2:
226 | using (var stream = response.GetResponseStream())
227 | {
228 | var result = DownStream(response_max, _web, stream, out _);
229 | if (result != null && _cache != null)
230 | {
231 | _cache.path.CreateDirectory();
232 | File.WriteAllBytes(_cache.file, result);
233 | }
234 | return new TaskResult(_web, result);
235 | }
236 | case 0:
237 | default:
238 | using (var stream = response.GetResponseStream())
239 | {
240 | return new TaskResult(_web);
241 | }
242 | }
243 | }
244 | }
245 | catch (Exception err)
246 | {
247 | if (err is WebException err_web && err_web.Response is HttpWebResponse response)
248 | {
249 | var web = new ResultResponse(response, err);
250 | Config.OnFail(this, web);
251 | action_fail?.Invoke(web);
252 | if (response.ContentLength > 0)
253 | {
254 | switch (resultMode)
255 | {
256 | case 1:
257 | using (var stream = response.GetResponseStream())
258 | {
259 | var data = DownStream(response.ContentLength, web, stream, out _);
260 | if (data == null) return new TaskResult(web);
261 | else
262 | {
263 | var encodings = option.encoding;
264 | if (encodings == null || option.autoencode) encodings = GetEncoding(response, data);
265 | var result = encodings.GetString(data);
266 | return new TaskResult(web, result);
267 | }
268 | }
269 | }
270 | }
271 | return new TaskResult(web);
272 | }
273 | else
274 | {
275 | var web = new ResultResponse(option.Url, err);
276 | Config.OnFail(this, web);
277 | action_fail?.Invoke(web);
278 | return new TaskResult(web);
279 | }
280 | }
281 | }
282 |
283 | class TaskResult
284 | {
285 | public TaskResult(ResultResponse _web)
286 | {
287 | type = 0;
288 | web = _web;
289 | }
290 | public TaskResult(ResultResponse _web, byte[]? _data)
291 | {
292 | type = 2;
293 | web = _web;
294 | data = _data;
295 | }
296 | public TaskResult(ResultResponse _web, string? _str)
297 | {
298 | type = 1;
299 | web = _web;
300 | str = _str;
301 | }
302 | public ResultResponse web { get; set; }
303 | public int type { get; set; }
304 | public string? str { get; set; }
305 | public byte[]? data { get; set; }
306 | }
307 |
308 | #region 请求头-帮助
309 |
310 | string GetTFName(string strItem, string replace = "-")
311 | {
312 | string strItemTarget = ""; //目标字符串
313 | for (int i = 0; i < strItem.Length; i++) //strItem是原始字符串
314 | {
315 | string temp = strItem[i].ToString();
316 | if (Regex.IsMatch(temp, "[A-Z]")) temp = replace + temp.ToLower();
317 | strItemTarget += temp;
318 | }
319 | return strItemTarget;
320 | }
321 | void SetHeader(out bool isContentType, HttpWebRequest req, List headers, CookieContainer cookies)
322 | {
323 | isContentType = true;
324 | foreach (var it in headers)
325 | {
326 | if (it.Value != null)
327 | {
328 | switch (it.Key.ToLower())
329 | {
330 | case "host":
331 | req.Host = it.Value;
332 | break;
333 | case "accept":
334 | req.Accept = it.Value;
335 | break;
336 | case "user-agent":
337 | req.UserAgent = it.Value;
338 | break;
339 | case "referer":
340 | req.Referer = it.Value;
341 | break;
342 | case "content-type":
343 | isContentType = false;
344 | req.ContentType = it.Value;
345 | break;
346 | case "cookie":
347 | if (it.Value != null)
348 | {
349 | if (it.Value.IndexOf(";") >= 0)
350 | {
351 | var Cookies = it.Value.Split(';');
352 | foreach (string cook in Cookies)
353 | {
354 | if (string.IsNullOrEmpty(cook)) continue;
355 | if (cook.IndexOf("expires") > 0) continue;
356 | cookies.SetCookies(req.RequestUri, cook);
357 | }
358 | }
359 | else cookies.SetCookies(req.RequestUri, it.Value);
360 | }
361 | break;
362 | default:
363 | req.Headers.Add(it.Key, it.Value);
364 | break;
365 | }
366 | }
367 | }
368 | }
369 |
370 | #endregion
371 |
372 | #region 响应流-帮助
373 |
374 | ///
375 | /// 获取域名IP
376 | ///
377 | public string? IP { get => option.IP; }
378 |
379 | public long Val = 0, Max = 0;
380 |
381 | byte[]? DownStream(long response_max, ResultResponse _web, Stream stream, out string? outfile, string? savePath = null, string? saveName = null)
382 | {
383 | Max = response_max;
384 | long response_val = 0;
385 | Val = response_val;
386 | Stream stream_read;
387 | if (savePath == null)
388 | {
389 | outfile = null;
390 | stream_read = new MemoryStream();
391 | }
392 | else
393 | {
394 | savePath = savePath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
395 | savePath.CreateDirectory();
396 | saveName ??= option.FileName(_web);
397 | outfile = savePath + saveName;
398 | stream_read = new FileStream(outfile, FileMode.Create);
399 | }
400 |
401 | using (stream_read)
402 | {
403 | byte[] buffer = new byte[Config.CacheSize];
404 | if (_responseProgres == null)
405 | {
406 | int rsize = 0;
407 | while ((rsize = stream.Read(buffer, 0, buffer.Length)) > 0)
408 | {
409 | stream_read.Write(buffer, 0, rsize);
410 | response_val += rsize;
411 | Val = response_val;
412 | }
413 | }
414 | else
415 | {
416 | _responseProgres(response_val, response_max);
417 | int rsize = 0;
418 | while ((rsize = stream.Read(buffer, 0, buffer.Length)) > 0)
419 | {
420 | stream_read.Write(buffer, 0, rsize);
421 | response_val += rsize;
422 | _responseProgres(response_val, response_max);
423 | Val = response_val;
424 | }
425 | }
426 | _web.OriginalSize = _web.Size = response_val;
427 | if (response_val > 0)
428 | {
429 | if (savePath == null) return GetByStream(_web, stream_read);
430 | else return null;
431 | }
432 | else
433 | {
434 | stream_read.Close();
435 | if (outfile != null) File.Delete(outfile);
436 | outfile = null;
437 | return null;
438 | }
439 | }
440 | }
441 |
442 | byte[] GetByStream(ResultResponse _web, Stream stream)
443 | {
444 | stream.Position = 0;
445 | using (var ms = new MemoryStream())
446 | {
447 | stream.CopyTo(ms);
448 | ms.Position = stream.Position = 0;
449 | string fileclass = "";
450 | try
451 | {
452 | using (var r = new BinaryReader(stream))
453 | {
454 | byte buffer = r.ReadByte();
455 | fileclass = buffer.ToString();
456 | buffer = r.ReadByte();
457 | fileclass += buffer.ToString();
458 | }
459 | }
460 | catch { }
461 | ms.Position = 0;
462 | if (fileclass == "31139")
463 | {
464 | try
465 | {
466 | byte[] data = Decompress(ms);
467 | _web.Size = data.Length;
468 | return data;
469 | }
470 | catch { }
471 | }
472 | ms.Position = 0;
473 | return ms.ToArray();
474 | }
475 | }
476 |
477 | Encoding GetEncoding(HttpWebResponse response, byte[] data)
478 | {
479 | var meta = Regex.Match(Encoding.Default.GetString(data), " 0)
481 | {
482 | var code = meta.Groups[1].Value.ToLower().Trim();
483 | if (code.Length > 2)
484 | {
485 | try
486 | {
487 | return Encoding.GetEncoding(code.Replace("\"", "").Replace("'", "").Replace(";", "").Replace("iso-8859-1", "gbk").Trim());
488 | }
489 | catch { }
490 | }
491 | }
492 | try
493 | {
494 | if (string.IsNullOrEmpty(response.CharacterSet)) return Encoding.UTF8;
495 | else return Encoding.GetEncoding(response.CharacterSet);
496 | }
497 | catch { return Encoding.UTF8; }
498 | }
499 |
500 | ///
501 | /// 解压字符串
502 | ///
503 | ///
504 | ///
505 | byte[] Decompress(MemoryStream ms)
506 | {
507 | using (var zip = new GZipStream(ms, CompressionMode.Decompress))
508 | {
509 | using (var msreader = new MemoryStream())
510 | {
511 | var buffer = new byte[Config.CacheSize];
512 | while (true)
513 | {
514 | var reader = zip.Read(buffer, 0, buffer.Length);
515 | if (reader <= 0) break;
516 | msreader.Write(buffer, 0, reader);
517 | }
518 | msreader.Position = 0;
519 | buffer = msreader.ToArray();
520 | return buffer;
521 | }
522 | }
523 | }
524 |
525 | internal HttpWebRequest CreateRequest()
526 | {
527 | var uri = option.Url;
528 |
529 | var cookies = new CookieContainer();
530 |
531 | #region SSL
532 |
533 | ServicePointManager.ServerCertificateValidationCallback ??= (_s, certificate, chain, sslPolicyErrors) => { return true; };
534 |
535 | #endregion
536 |
537 | var req = (HttpWebRequest)WebRequest.Create(uri);
538 |
539 | if (option.proxy != null) req.Proxy = option.proxy;
540 | else req.Proxy = Config._proxy;
541 | req.Method = option.method.ToString().ToUpper();
542 | req.AutomaticDecompression = Config.DecompressionMethod;
543 | req.CookieContainer = cookies;
544 | req.Host = uri.Host;
545 | if (_range != null) req.AddRange(_range[0], _range[1]);
546 |
547 | if (option.redirect) req.AllowAutoRedirect = option.redirect;
548 | else req.AllowAutoRedirect = Config.Redirect;
549 | var encoding = option.encoding ?? Encoding.UTF8;
550 | if (option.timeout > 0) req.Timeout = option.timeout;
551 |
552 | req.Credentials = CredentialCache.DefaultCredentials;
553 | req.UserAgent = Config.UserAgent;
554 |
555 | bool setContentType = true;
556 | if (Config.headers != null && Config.headers.Count > 0) SetHeader(out setContentType, req, Config.headers, cookies);
557 | if (option.header != null && option.header.Count > 0) SetHeader(out setContentType, req, option.header, cookies);
558 |
559 | #region 准备上传数据
560 |
561 | if (option.method != HttpMethod.Get && option.method != HttpMethod.Head)
562 | {
563 | if (!string.IsNullOrEmpty(option.datastr))
564 | {
565 | if (setContentType) req.ContentType = "text/plain";
566 |
567 | var bs = encoding.GetBytes(option.datastr);
568 | req.ContentLength = bs.Length;
569 | using (var stream = req.GetRequestStream())
570 | {
571 | stream.Write(bs, 0, bs.Length);
572 | }
573 | }
574 | else if (option.file != null && option.file.Count > 0)
575 | {
576 | string boundary = 8.RandomString();
577 | req.ContentType = "multipart/form-data; boundary=" + boundary;
578 |
579 | byte[] startbyteOnes = encoding.GetBytes("--" + boundary + "\r\n"),
580 | startbytes = encoding.GetBytes("\r\n--" + boundary + "\r\n"),
581 | endbytes = encoding.GetBytes("\r\n--" + boundary + "--\r\n");
582 |
583 | var writeDATA = new List((option.data == null ? 0 : option.data.Count * 2) + option.file.Count * 3 + 1);
584 |
585 | int countB = 0;
586 |
587 | #region 规划文件大小
588 |
589 | if (option.data != null && option.data.Count > 0)
590 | {
591 | foreach (var it in option.data)
592 | {
593 | if (it.Value == null) continue;
594 | if (countB == 0) writeDATA.Add(startbyteOnes);
595 | else writeDATA.Add(startbytes);
596 | countB++;
597 | string separator = string.Format("Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}", it.Key, it.Value);
598 | writeDATA.Add(encoding.GetBytes(separator));
599 | }
600 | }
601 |
602 | foreach (var file in option.file)
603 | {
604 | if (countB == 0) writeDATA.Add(startbyteOnes);
605 | else writeDATA.Add(startbytes);
606 | countB++;
607 | string separator = string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n", file.Name, file.FileName, file.ContentType);
608 | writeDATA.Add(encoding.GetBytes(separator));
609 | writeDATA.Add(null);
610 | }
611 | writeDATA.Add(endbytes);
612 |
613 | #endregion
614 |
615 | long size = writeDATA.Sum(it => it != null ? it.Length : 0) + option.file.Sum(it => it.Size);
616 | req.ContentLength = size;
617 |
618 | #region 注入进度
619 |
620 | _requestProgresMax?.Invoke(size);
621 | if (_requestProgres == null)
622 | {
623 | using (var stream = req.GetRequestStream())
624 | {
625 | int fileIndex = 0;
626 | foreach (var it in writeDATA)
627 | {
628 | if (it == null)
629 | {
630 | var file = option.file[fileIndex];
631 | fileIndex++;
632 | using (file.Stream)
633 | {
634 | file.Stream.Position = 0;
635 | int bytesRead = 0;
636 | byte[] buffer = new byte[Config.CacheSize];
637 | while ((bytesRead = file.Stream.Read(buffer, 0, buffer.Length)) > 0)
638 | {
639 | stream.Write(buffer, 0, bytesRead);
640 | }
641 | }
642 | }
643 | else stream.Write(it, 0, it.Length);
644 | }
645 | }
646 | }
647 | else
648 | {
649 | long request_value = 0;
650 | _requestProgres(request_value, size);
651 | using (var stream = req.GetRequestStream())
652 | {
653 | int fileIndex = 0;
654 | foreach (var it in writeDATA)
655 | {
656 | if (it == null)
657 | {
658 | var file = option.file[fileIndex];
659 | fileIndex++;
660 | using (file.Stream)
661 | {
662 | file.Stream.Position = 0;
663 | int bytesRead = 0;
664 | byte[] buffer = new byte[Config.CacheSize];
665 | while ((bytesRead = file.Stream.Read(buffer, 0, buffer.Length)) > 0)
666 | {
667 | stream.Write(buffer, 0, bytesRead);
668 | request_value += bytesRead;
669 | _requestProgres(request_value, size);
670 | }
671 | }
672 | }
673 | else
674 | {
675 | stream.Write(it, 0, it.Length);
676 | request_value += it.Length;
677 | _requestProgres(request_value, size);
678 | }
679 | }
680 | }
681 | }
682 |
683 | #endregion
684 | }
685 | else if (option.data != null && option.data.Count > 0)
686 | {
687 | if (setContentType) req.ContentType = "application/x-www-form-urlencoded";
688 |
689 | var param_ = new List(option.data.Count);
690 | foreach (var it in option.data)
691 | {
692 | if (it.Value == null) continue;
693 | param_.Add(it.ToStringEscape());
694 | }
695 |
696 | var bs = encoding.GetBytes(string.Join("&", param_));
697 | req.ContentLength = bs.Length;
698 | using (var stream = req.GetRequestStream())
699 | {
700 | stream.Write(bs, 0, bs.Length);
701 | }
702 | }
703 | }
704 |
705 | #endregion
706 |
707 | return req;
708 | }
709 |
710 | #endregion
711 | }
712 | }
--------------------------------------------------------------------------------