├── .gitignore
├── LICENSE
├── README.md
├── UpYun.NETCore.sln
├── UpYun.NETCore
├── FolderItem.cs
├── UpYun.NETCore.csproj
├── UpYunClient.cs
└── UpYunResult.cs
└── UpYun.NetCore.Tests
├── Program.cs
└── UpYun.NetCore.Tests.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # .NET Core
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 | **/Properties/launchSettings.json
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # Visual Studio code coverage results
117 | *.coverage
118 | *.coveragexml
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignorable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | orleans.codegen.cs
203 |
204 | # Since there are multiple workflows, uncomment next line to ignore bower_components
205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
206 | #bower_components/
207 |
208 | # RIA/Silverlight projects
209 | Generated_Code/
210 |
211 | # Backup & report files from converting an old project file
212 | # to a newer Visual Studio version. Backup files are not needed,
213 | # because we have git ;-)
214 | _UpgradeReport_Files/
215 | Backup*/
216 | UpgradeLog*.XML
217 | UpgradeLog*.htm
218 |
219 | # SQL Server files
220 | *.mdf
221 | *.ldf
222 | *.ndf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 | node_modules/
238 |
239 | # Typescript v1 declaration files
240 | typings/
241 |
242 | # Visual Studio 6 build log
243 | *.plg
244 |
245 | # Visual Studio 6 workspace options file
246 | *.opt
247 |
248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
249 | *.vbw
250 |
251 | # Visual Studio LightSwitch build output
252 | **/*.HTMLClient/GeneratedArtifacts
253 | **/*.DesktopClient/GeneratedArtifacts
254 | **/*.DesktopClient/ModelManifest.xml
255 | **/*.Server/GeneratedArtifacts
256 | **/*.Server/ModelManifest.xml
257 | _Pvt_Extensions
258 |
259 | # Paket dependency manager
260 | .paket/paket.exe
261 | paket-files/
262 |
263 | # FAKE - F# Make
264 | .fake/
265 |
266 | # JetBrains Rider
267 | .idea/
268 | *.sln.iml
269 |
270 | # CodeRush
271 | .cr/
272 |
273 | # Python Tools for Visual Studio (PTVS)
274 | __pycache__/
275 | *.pyc
276 |
277 | # Cake - Uncomment if you are using it
278 | # tools/**
279 | # !tools/packages.config
280 |
281 | # Telerik's JustMock configuration file
282 | *.jmconfig
283 |
284 | # BizTalk build output
285 | *.btp.cs
286 | *.btm.cs
287 | *.odx.cs
288 | *.xsd.cs
289 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UpYun.NETCore
2 | 又拍云 SDK .net Core版
3 |
4 | Nuget安装指令:
5 | ```C#
6 | Install-Package UpYun.NETCore
7 | ```
8 | 示例代码:
9 | ```C#
10 | UpYunClient upyun = new UpYunClient(bucketName, username, password, httpClientFactory);
11 | byte[] bytes = Encoding.UTF8.GetBytes("www.youzack.com");
12 | var a = await upyun.WriteFileAsync("/test.txt", bytes, true);
13 | ```
--------------------------------------------------------------------------------
/UpYun.NETCore.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26403.7
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpYun.NETCore", "UpYun.NETCore\UpYun.NETCore.csproj", "{FFF759BE-B5C5-4891-9EC8-C76C6B329595}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpYun.NetCore.Tests", "UpYun.NetCore.Tests\UpYun.NetCore.Tests.csproj", "{771E11FB-2AC9-48DE-A01B-9BB01A227CC9}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {FFF759BE-B5C5-4891-9EC8-C76C6B329595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {FFF759BE-B5C5-4891-9EC8-C76C6B329595}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {FFF759BE-B5C5-4891-9EC8-C76C6B329595}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {FFF759BE-B5C5-4891-9EC8-C76C6B329595}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {771E11FB-2AC9-48DE-A01B-9BB01A227CC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {771E11FB-2AC9-48DE-A01B-9BB01A227CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {771E11FB-2AC9-48DE-A01B-9BB01A227CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {771E11FB-2AC9-48DE-A01B-9BB01A227CC9}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/UpYun.NETCore/FolderItem.cs:
--------------------------------------------------------------------------------
1 | namespace UpYun.NETCore
2 | {
3 | //目录条目类
4 | public class FolderItem
5 | {
6 | public string filename;
7 | public string filetype;
8 | public int size;
9 | public int number;
10 | public FolderItem(string filename, string filetype, int size, int number)
11 | {
12 | this.filename = filename;
13 | this.filetype = filetype;
14 | this.size = size;
15 | this.number = number;
16 | }
17 |
18 | public override string ToString()
19 | {
20 | return $"filename={filename},filetype={filetype},size={size},number={number}";
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/UpYun.NETCore/UpYun.NETCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | UpYun .net core SDK. http://www.upyun.com by yangzhongke
6 | https://github.com/yangzhongke/UpYun.NETCore
7 | True
8 | https://github.com/yangzhongke/UpYun.NETCore
9 | 1.1.2
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/UpYun.NETCore/UpYunClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Net.Http;
6 | using System.Security.Cryptography;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | namespace UpYun.NETCore
12 | {
13 | public class UpYunClient
14 | {
15 | private IHttpClientFactory httpClientFactory;
16 |
17 | private string bucketname;
18 | private string username;
19 | private string password;
20 | private bool upAuth = false;
21 | private string api_domain = "v0.api.upyun.com";
22 | private string DL = "/";
23 | private Dictionary tmp_infos = new Dictionary();
24 | private string file_secret;
25 | private string content_md5;
26 | private bool auto_mkdir = false;
27 |
28 |
29 | public string version() { return "1.0.1"; }
30 |
31 | /**
32 | * 初始化 UpYun 存储接口
33 | * @param $bucketname 空间名称
34 | * @param $username 操作员名称
35 | * @param $password 密码
36 | * return UpYun object
37 | */
38 | public UpYunClient(string bucketname, string username, string password, IHttpClientFactory httpClientFactory)
39 | {
40 | this.bucketname = bucketname;
41 | this.username = username;
42 | this.password = password;
43 | this.httpClientFactory = httpClientFactory;
44 | }
45 |
46 | ///
47 | /// 是否使用Https协议
48 | ///
49 | public bool IsHttps { get; set; }=true;
50 |
51 | /**
52 | * 切换 API 接口的域名
53 | * @param $domain {默认 v0.api.upyun.com 自动识别, v1.api.upyun.com 电信, v2.api.upyun.com 联通, v3.api.upyun.com 移动}
54 | * return null;
55 | */
56 | public void setApiDomain(string domain)
57 | {
58 | this.api_domain = domain;
59 | }
60 |
61 | /**
62 | * 是否启用 又拍签名认证
63 | * @param upAuth {默认 false 不启用(直接使用basic auth),true 启用又拍签名认证}
64 | * return null;
65 | */
66 | public void setAuthType(bool upAuth)
67 | {
68 | this.upAuth = upAuth;
69 | }
70 |
71 | private async Task UpYunAuthAsync(ByteArrayContent requestContent,string method,string uri, CancellationToken cancellationToken = default)
72 | {
73 | DateTime dt = DateTime.UtcNow;
74 | string date = dt.ToString("ddd, dd MMM yyyy HH':'mm':'ss 'GMT'", new CultureInfo("en-US"));
75 |
76 | requestContent.Headers.Add("Date", date);
77 | string body = await requestContent.ReadAsStringAsync();
78 | string auth;
79 | if (!string.IsNullOrEmpty(body))
80 | {
81 | byte[] bytesContent = await requestContent.ReadAsByteArrayAsync();
82 | auth = md5(method + '&' + uri + '&' + date + '&' + bytesContent.Length + '&' + md5(this.password));
83 | }
84 | else
85 | {
86 | auth = md5(method + '&' + uri + '&' + date + '&' + 0 + '&' + md5(this.password));
87 | }
88 | requestContent.Headers.Add("Authorization", "UpYun " + this.username + ':' + auth);
89 | }
90 |
91 | private string md5(string str)
92 | {
93 | using (MD5 m = MD5.Create())
94 | {
95 | byte[] s = m.ComputeHash(Encoding.UTF8.GetBytes(str));
96 | string resule = BitConverter.ToString(s);
97 | resule = resule.Replace("-", "");
98 | return resule.ToLower();
99 | }
100 | }
101 | private async Task DeleteAsync(string path, Dictionary headers, CancellationToken cancellationToken = default)
102 | {
103 | var resp = await NewWorkAsync("DELETE", DL + this.bucketname + path, null, headers,cancellationToken);
104 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
105 | {
106 | return UpYunResult.OK;
107 | }
108 | else
109 | {
110 | return await UpYunResult.CreateErrorAsync(resp);
111 | }
112 | }
113 |
114 |
115 | private async Task NewWorkAsync(string method, string url, byte[] postData, Dictionary headers,
116 | CancellationToken cancellationToken=default)
117 | {
118 | HttpClient httpClient = httpClientFactory.CreateClient();
119 | if(postData==null)
120 | {
121 | postData = new byte[0];
122 | }
123 | using (ByteArrayContent byteContent = new ByteArrayContent(postData))
124 | {
125 | string protocol = IsHttps ? "https://" : "http://";
126 | httpClient.BaseAddress = new Uri(protocol + api_domain);
127 | if (this.auto_mkdir == true)
128 | {
129 | byteContent.Headers.Add("mkdir", "true");
130 | this.auto_mkdir = false;
131 | }
132 |
133 | if (postData != null)
134 | {
135 | if (this.content_md5 != null)
136 | {
137 | byteContent.Headers.Add("Content-MD5", this.content_md5);
138 | this.content_md5 = null;
139 | }
140 | if (this.file_secret != null)
141 | {
142 | byteContent.Headers.Add("Content-Secret", this.file_secret);
143 | this.file_secret = null;
144 | }
145 | }
146 |
147 | if (this.upAuth)
148 | {
149 | await UpYunAuthAsync(byteContent, method, url, cancellationToken);
150 | }
151 | else
152 | {
153 | //byteContent.Headers.Add("Authorization", "Basic " +
154 | //Convert.ToBase64String(new System.Text.ASCIIEncoding().GetBytes(this.username + ":" + this.password)));
155 | var value = Convert.ToBase64String(new System.Text.ASCIIEncoding().GetBytes(this.username + ":" + this.password));
156 | httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", value);
157 | }
158 | foreach (var kv in headers)
159 | {
160 | byteContent.Headers.Add(kv.Key, kv.Value.ToString());
161 | }
162 |
163 | HttpResponseMessage responseMsg;
164 | if ("Get".Equals(method, StringComparison.OrdinalIgnoreCase))
165 | {
166 | responseMsg = await httpClient.GetAsync(url, cancellationToken);
167 | }
168 | else if ("Post".Equals(method, StringComparison.OrdinalIgnoreCase))
169 | {
170 | responseMsg = await httpClient.PostAsync(url, byteContent, cancellationToken);
171 | }
172 | else if ("PUT".Equals(method, StringComparison.OrdinalIgnoreCase))
173 | {
174 | responseMsg = await httpClient.PutAsync(url, byteContent, cancellationToken);
175 | }
176 | else if ("Delete".Equals(method, StringComparison.OrdinalIgnoreCase))
177 | {
178 | responseMsg = await httpClient.DeleteAsync(url, cancellationToken);
179 | }
180 | else
181 | {
182 | throw new Exception("未知method:" + method);
183 | }
184 |
185 | this.tmp_infos = new Dictionary();
186 | foreach (var header in responseMsg.Headers)
187 | {
188 | if (header.Key.Length > 7 && header.Key.Substring(0, 7) == "x-upyun")
189 | {
190 | this.tmp_infos.Add(header.Key, header.Value);
191 | }
192 | }
193 |
194 | return responseMsg;
195 | }
196 | }
197 |
198 | /**
199 | * 获取总体空间的占用信息
200 | * return 空间占用量,失败返回 null
201 | */
202 |
203 | public async Task> GetFolderUsageAsync(string url, CancellationToken cancellationToken = default)
204 | {
205 | Dictionary headers = new Dictionary();
206 | using (var resp = await NewWorkAsync("GET", DL + this.bucketname + url + "?usage", null, headers, cancellationToken))
207 | {
208 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
209 | {
210 | string strhtml = await resp.Content.ReadAsStringAsync();
211 | long size = long.Parse(strhtml);
212 | return size;
213 | }
214 | else
215 | {
216 | return await UpYunResult.CreateErrorAsync(resp);
217 | }
218 | }
219 |
220 | }
221 |
222 | /**
223 | * 获取某个子目录的占用信息
224 | * @param $path 目标路径
225 | * return 空间占用量,失败返回 null
226 | */
227 | public Task> GetBucketUsageAsync(CancellationToken cancellationToken = default)
228 | {
229 | return GetFolderUsageAsync("/", cancellationToken);
230 | }
231 | /**
232 | * 创建目录
233 | * @param $path 目录路径
234 | * return true or false
235 | */
236 | public async Task MkDirAsync(string path, bool auto_mkdir, CancellationToken cancellationToken = default)
237 | {
238 | this.auto_mkdir = auto_mkdir;
239 | Dictionary headers = new Dictionary();
240 | headers.Add("folder", "create");
241 |
242 | using (var resp = await NewWorkAsync("POST", DL + this.bucketname + path, null, headers, cancellationToken))
243 | {
244 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
245 | {
246 | return UpYunResult.OK;
247 | }
248 | else
249 | {
250 | return await UpYunResult.CreateErrorAsync(resp);
251 | }
252 | }
253 | }
254 |
255 | /**
256 | * 删除目录
257 | * @param $path 目录路径
258 | * return true or false
259 | */
260 | public Task RmDirAsync(string path, CancellationToken cancellationToken = default)
261 | {
262 | Dictionary headers = new Dictionary();
263 | return DeleteAsync(path, headers, cancellationToken);
264 | }
265 |
266 | /**
267 | * 读取目录列表
268 | * @param $path 目录路径
269 | * return array 数组 或 null
270 | */
271 | public async Task>> ReadDirAsync(string url, CancellationToken cancellationToken = default)
272 | {
273 | Dictionary headers = new Dictionary();
274 | //headers["Accept"] = "application/json";
275 | byte[] a = null;
276 | using (var resp = await NewWorkAsync("GET", DL + this.bucketname + url, a, headers, cancellationToken))
277 | {
278 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
279 | {
280 | string strhtml = await resp.Content.ReadAsStringAsync();
281 | strhtml = strhtml.Replace("\t", "\\");
282 | strhtml = strhtml.Replace("\n", "\\");
283 | string[] ss = strhtml.Split('\\', StringSplitOptions.RemoveEmptyEntries);
284 | int i = 0;
285 | List list = new List();
286 | while (i < ss.Length)
287 | {
288 | FolderItem fi = new FolderItem(ss[i], ss[i + 1], int.Parse(ss[i + 2]), int.Parse(ss[i + 3]));
289 | list.Add(fi);
290 | i += 4;
291 | }
292 | return list;
293 | }
294 | else
295 | {
296 | return await UpYunResult>.CreateErrorAsync(resp);
297 | }
298 | }
299 |
300 | }
301 |
302 |
303 | /**
304 | * 上传文件
305 | * @param $file 文件路径(包含文件名)
306 | * @param $datas 文件内容 或 文件IO数据流
307 | * return true or false
308 | */
309 | public async Task WriteFileAsync(string path, byte[] data, bool auto_mkdir, CancellationToken cancellationToken = default)
310 | {
311 | Dictionary headers = new Dictionary();
312 | this.auto_mkdir = auto_mkdir;
313 | using (var resp = await NewWorkAsync("POST", DL + this.bucketname + path, data, headers, cancellationToken))
314 | {
315 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
316 | {
317 | return UpYunResult.OK;
318 | }
319 | else
320 | {
321 | return await UpYunResult.CreateErrorAsync(resp);
322 | }
323 | }
324 |
325 | }
326 | /**
327 | * 删除文件
328 | * @param $file 文件路径(包含文件名)
329 | * return true or false
330 | */
331 | public Task DeleteFileAsync(string path, CancellationToken cancellationToken = default)
332 | {
333 | Dictionary headers = new Dictionary();
334 | return DeleteAsync(path, headers, cancellationToken);
335 | }
336 |
337 | ///
338 | /// 移动文件
339 | ///
340 | /// 源路径
341 | /// 目标路径
342 | ///
343 | ///
344 | public async Task MoveFileAsync(string path, string dest, CancellationToken cancellationToken = default)
345 | {
346 | Dictionary headers = new Dictionary();
347 | headers["X-Upyun-Move-Source"] = DL + this.bucketname + Uri.EscapeUriString(path);
348 | var resp = await NewWorkAsync("PUT", DL + this.bucketname + Uri.EscapeUriString(dest), null, headers, cancellationToken);
349 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
350 | {
351 | return UpYunResult.OK;
352 | }
353 | else
354 | {
355 | return await UpYunResult.CreateErrorAsync(resp);
356 | }
357 | }
358 |
359 | ///
360 | /// 重命名文件
361 | ///
362 | /// 源路径
363 | /// 目标路径
364 | ///
365 | ///
366 |
367 | public Task RenameFileAsync(string path, string dest, CancellationToken cancellationToken = default)
368 | {
369 | return MoveFileAsync(path, dest, cancellationToken);
370 | }
371 |
372 |
373 | /**
374 | * 读取文件
375 | * @param $file 文件路径(包含文件名)
376 | * @param $output_file 可传递文件IO数据流(默认为 null,结果返回文件内容,如设置文件数据流,将返回 true or false)
377 | * return 文件内容 或 null
378 | */
379 | public async Task> ReadFileAsync(string path, CancellationToken cancellationToken = default)
380 | {
381 | Dictionary headers = new Dictionary();
382 | byte[] a = null;
383 |
384 | using (var resp = await NewWorkAsync("GET", DL + this.bucketname + path, a, headers, cancellationToken))
385 | {
386 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
387 | {
388 | return await resp.Content.ReadAsByteArrayAsync();
389 | }
390 | else
391 | {
392 | return await UpYunResult.CreateErrorAsync(resp);
393 | }
394 | }
395 | }
396 | /**
397 | * 设置待上传文件的 Content-MD5 值(如又拍云服务端收到的文件MD5值与用户设置的不一致,将回报 406 Not Acceptable 错误)
398 | * @param $str (文件 MD5 校验码)
399 | * return null;
400 | */
401 | public void SetContentMD5(string str)
402 | {
403 | this.content_md5 = str;
404 | }
405 | /**
406 | * 设置待上传文件的 访问密钥(注意:仅支持图片空!,设置密钥后,无法根据原文件URL直接访问,需带 URL 后面加上 (缩略图间隔标志符+密钥) 进行访问)
407 | * 如缩略图间隔标志符为 ! ,密钥为 bac,上传文件路径为 /folder/test.jpg ,那么该图片的对外访问地址为: http://空间域名/folder/test.jpg!bac
408 | * @param $str (文件 MD5 校验码)
409 | * return null;
410 | */
411 | public void SetFileSecret(string str)
412 | {
413 | this.file_secret = str;
414 | }
415 | /**
416 | * 获取文件信息
417 | * @param $file 文件路径(包含文件名)
418 | * return array('type'=> file | folder, 'size'=> file size, 'date'=> unix time) 或 null
419 | */
420 | public async Task>> GetFileInfoAsync(string file, CancellationToken cancellationToken = default)
421 | {
422 | Dictionary headers = new Dictionary();
423 | byte[] a = null;
424 | using (var resp = await NewWorkAsync("HEAD", DL + this.bucketname + file, a, headers, cancellationToken))
425 | {
426 | if (resp.StatusCode == System.Net.HttpStatusCode.OK)
427 | {
428 | Dictionary respHeaders =new Dictionary();
429 | if(tmp_infos.ContainsKey("x-upyun-file-type")&&tmp_infos.ContainsKey("x-upyun-file-size")
430 | && tmp_infos.ContainsKey("x-upyun-file-date"))
431 | {
432 | respHeaders.Add("type", tmp_infos["x-upyun-file-type"]);
433 | respHeaders.Add("size", tmp_infos["x-upyun-file-size"]);
434 | respHeaders.Add("date", tmp_infos["x-upyun-file-date"]);
435 | }
436 | return respHeaders;
437 | }
438 | else
439 | {
440 | return await UpYunResult>.CreateErrorAsync(resp);
441 | }
442 | }
443 | }
444 | //获取上传后的图片信息(仅图片空间有返回数据)
445 | public object GetWritedFileInfo(string key)
446 | {
447 | if (this.tmp_infos == new Dictionary()) return "";
448 | return this.tmp_infos[key];
449 | }
450 | //计算文件的MD5码
451 | public static string md5_file(string pathName)
452 | {
453 | string strResult;
454 | string strHashData;
455 |
456 | byte[] arrbytHashValue;
457 | using (var md5 = MD5.Create())
458 | using (FileStream oFileStream = new FileStream(pathName, System.IO.FileMode.Open,
459 | System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
460 | {
461 | arrbytHashValue = md5.ComputeHash(oFileStream);//计算指定Stream 对象的哈希值
462 | //由以连字符分隔的十六进制对构成的String,其中每一对表示value 中对应的元素;例如“F-2C-4A”
463 | strHashData = System.BitConverter.ToString(arrbytHashValue);
464 | //替换-
465 | strHashData = strHashData.Replace("-", "");
466 | strResult = strHashData;
467 | return strResult.ToLower();
468 | }
469 | }
470 | }
471 |
472 | }
473 |
--------------------------------------------------------------------------------
/UpYun.NETCore/UpYunResult.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Text.Json;
3 | using System.Threading.Tasks;
4 |
5 | namespace UpYun.NETCore
6 | {
7 | public class UpYunResult
8 | {
9 | public bool IsOK { get; set; }
10 | public string Msg { get; set; }
11 | public int Code { get; set; }
12 | public string Id { get; set; }
13 |
14 | public static readonly UpYunResult OK = new UpYunResult { IsOK=true};
15 | public static async Task CreateErrorAsync(HttpResponseMessage resp)
16 | {
17 | string body = await resp.Content.ReadAsStringAsync();
18 | JsonSerializerOptions options = new JsonSerializerOptions();
19 | options.PropertyNameCaseInsensitive = true;
20 | UpYunResult result = JsonSerializer.Deserialize(body, options);
21 | result.IsOK = false;
22 | return result;
23 | }
24 |
25 | public override string ToString()
26 | {
27 | return $"IsOk={IsOK},Msg={Msg},Code={Code},Id={Id}";
28 | }
29 | }
30 |
31 | public class UpYunResult
32 | {
33 | public bool IsOK { get; set; }
34 | public string Msg { get; set; }
35 | public int Code { get; set; }
36 | public string Id { get; set; }
37 |
38 | public T Value { get; set; }
39 |
40 | public static UpYunResult OK(T value)
41 | {
42 | UpYunResult result = new UpYunResult();
43 | result.IsOK = true;
44 | result.Value = value;
45 | return result;
46 | }
47 |
48 | public static async Task> CreateErrorAsync(HttpResponseMessage resp)
49 | {
50 | string body = await resp.Content.ReadAsStringAsync();
51 | var result = JsonSerializer.Deserialize>(body);
52 | result.IsOK = false;
53 | return result;
54 | }
55 |
56 | public static implicit operator UpYunResult(T value)
57 | {
58 | return new UpYunResult { IsOK=true,Value=value};
59 | }
60 |
61 | public override string ToString()
62 | {
63 | return $"IsOk={IsOK},Msg={Msg},Code={Code},Id={Id},Value={Value}";
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/UpYun.NetCore.Tests/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System;
3 | using System.IO;
4 | using System.Net.Http;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using UpYun.NETCore;
8 |
9 | namespace UpYun.NetCore.Tests
10 | {
11 | class Program
12 | {
13 | static async Task Main(string[] args)
14 | {
15 | string bucketName = args[0];
16 | string username = args[1];
17 | string password = args[2];
18 |
19 | ServiceCollection services = new ServiceCollection();
20 | services.AddHttpClient();
21 | using (var sp = services.BuildServiceProvider())
22 | {
23 | var httpClientFactory = sp.GetRequiredService();
24 | UpYunClient upyun = new UpYunClient(bucketName, username, password, httpClientFactory);
25 | /*
26 | byte[] bytes = Encoding.UTF8.GetBytes("www.youzack.com");
27 | var a = await upyun.WriteFileAsync("/test.txt", bytes, true);
28 | Console.WriteLine(a);*/
29 | /*
30 | var r = await upyun.RenameFileAsync("/02b2b5f0-3484-11e6-81f3-ccb34c23190a%20%5Blow%5D.mp3", "/02b2b5f0-3484-11e6-81f3-ccb34c23190a.mp3");
31 | Console.WriteLine(r);*/
32 | await ListAsync(upyun, "/");
33 | //var r = await upyun.RenameFileAsync("/电脑02b2b5f0-3484-11e6-81f3-ccb34c23190a [low].mp3", "/电脑02b2b5f0-3484-11e6-81f3-ccb34c23190a.mp3");
34 | //Console.WriteLine(r);
35 | }
36 | Console.ReadLine(); Console.ReadLine();
37 | }
38 |
39 | static void WriteLog(string s)
40 | {
41 | File.AppendAllText("d:/1.txt",s+"\r\n");
42 | }
43 |
44 | static async Task ListAsync(UpYunClient upyun, string folder)
45 | {
46 | var items = await upyun.ReadDirAsync(folder);
47 | foreach (var item in items.Value)
48 | {
49 | string fullPath;
50 | if(!folder.EndsWith('/'))
51 | {
52 | folder = folder + "/";
53 | }
54 | fullPath=folder + item.filename;
55 | //Console.WriteLine(fullPath);
56 | if(fullPath.Contains(" [low]"))
57 | {
58 | string newPath = fullPath.Replace(" [low]", "");
59 | try
60 | {
61 | var r = await upyun.RenameFileAsync(fullPath, newPath);
62 | if (r.IsOK)
63 | {
64 | Console.WriteLine("成功:" + fullPath + "," + newPath);
65 | }
66 | else
67 | {
68 | Console.ForegroundColor = ConsoleColor.Red;
69 | Console.BackgroundColor = ConsoleColor.White;
70 | Console.WriteLine("失败:" + fullPath + "," + newPath + "," + r);
71 | WriteLog("失败:" + fullPath + "," + newPath + "," + r);
72 | Console.ResetColor();
73 | }
74 | }
75 | catch(Exception ex)
76 | {
77 | Console.ForegroundColor = ConsoleColor.Red;
78 | Console.BackgroundColor = ConsoleColor.White;
79 | Console.WriteLine("失败:" + fullPath + "," + newPath + "," + ex);
80 | WriteLog("失败:" + fullPath + "," + newPath + "," + ex);
81 | Console.ResetColor();
82 | }
83 | }
84 | if(item.filetype=="F")
85 | {
86 | await ListAsync(upyun, fullPath);
87 | }
88 | }
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/UpYun.NetCore.Tests/UpYun.NetCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------