├── .gitattributes ├── .gitignore ├── FastDFSNetCore.sln ├── LICENSE ├── README.md ├── azure-pipelines.yml └── src ├── FastDFS.Test ├── FDFSHelper.cs ├── FastDFS.Test.csproj ├── Program.cs └── testimage │ ├── 0.jpg │ ├── 002.jpg │ ├── 004.jpg │ ├── 1.jpg │ └── 2c8f484465db50d6dc9c28f301c59172_big.jpg └── FastDFS ├── Client ├── APPEND_FILE.cs ├── Connection.cs ├── ConnectionManager.cs ├── DELETE_FILE.cs ├── DOWNLOAD_FILE.cs ├── FDFSConfig.cs ├── FDFSConstants.cs ├── FDFSException.cs ├── FDFSHeader.cs ├── FDFSRequest.cs ├── FDFSResponse.cs ├── FastDFSClient.cs ├── Pool.cs ├── QUERY_FILE_INFO.cs ├── QUERY_STORE_WITHOUT_GROUP_ONE.cs ├── QUERY_STORE_WITH_GROUP_ONE.cs ├── QUERY_UPDATE.cs ├── SocketEx.cs ├── StorageNode.cs ├── UPLOAD_APPEND_FILE.cs ├── UPLOAD_FILE.cs └── Util.cs ├── FastDFS.csproj └── Properties └── AssemblyInfo.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /FastDFSNetCore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastDFS", "src\FastDFS\FastDFS.csproj", "{EBB74999-FC55-444A-B2D4-333A2EA42A16}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastDFS.Test", "src\FastDFS.Test\FastDFS.Test.csproj", "{F01CA851-B64B-499E-A89A-9340264CE3D8}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5335D02C-921E-4996-A518-CAAB254619DD}" 11 | ProjectSection(SolutionItems) = preProject 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {EBB74999-FC55-444A-B2D4-333A2EA42A16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {EBB74999-FC55-444A-B2D4-333A2EA42A16}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {EBB74999-FC55-444A-B2D4-333A2EA42A16}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {EBB74999-FC55-444A-B2D4-333A2EA42A16}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {F01CA851-B64B-499E-A89A-9340264CE3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {F01CA851-B64B-499E-A89A-9340264CE3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {F01CA851-B64B-499E-A89A-9340264CE3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {F01CA851-B64B-499E-A89A-9340264CE3D8}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {2B04F661-F25F-4192-A453-8D4EAEACC8E0} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastDFSNetCore 2 | 3 | [![Build Status](https://dev.azure.com/q568022847/FastDFSNetCore/_apis/build/status/caozhiyuan.FastDFSNetCore?branchName=master)](https://dev.azure.com/q568022847/FastDFSNetCore/_build/latest?definitionId=5&branchName=master) 4 | 5 | ## Nuget Packages 6 | 7 | | Package | NuGet Stable | Downloads | 8 | | ------- | ------------ | --------- | 9 | | [FastDFSNetCore](https://www.nuget.org/packages/FastDFSNetCore/) | [![FastDFSNetCore](https://img.shields.io/nuget/v/FastDFSNetCore.svg)](https://www.nuget.org/packages/FastDFSNetCore/) | [![FastDFSNetCore](https://img.shields.io/nuget/dt/FastDFSNetCore.svg)](https://www.nuget.org/packages/FastDFSNetCore/) | 10 | 11 | ## Basic Usage 12 | 13 | see [Program.cs](https://github.com/caozhiyuan/FastDFSNetCore/blob/master/src/FastDFS.Test/Program.cs) 14 | 15 | 16 | ## Ngx_fastdfs_module 17 | 18 | 19 | ``` nginx 20 | server { 21 | listen 80; 22 | 23 | location / { 24 | root /path/to/fdfsstore/fdfs_storage/data; 25 | } 26 | 27 | location /group1/M00 { 28 | root /path/to/fdfsstore/fdfs_storage/data; 29 | ngx_fastdfs_module; 30 | } 31 | 32 | location ~* /group1/M00/(.*?)_([0-9]+)X([0-9]+)\.(jpeg|jpg|gif|png)$ { 33 | root /path/to/fdfsstore/fdfs_storage/data; 34 | set $h $2; 35 | set $w $3; 36 | rewrite /group1/M00/(.*?)_([0-9]+)X([0-9]+)\.(jpeg|jpg|gif|png)$ /$1.$4 break; 37 | image_filter_buffer 10M; 38 | image_filter resize $h $w; 39 | } 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | - refs/tags/* 4 | 5 | jobs: 6 | - job: Windows 7 | pool: 8 | vmImage: 'windows-2019' 9 | steps: 10 | - script: | 11 | dotnet build -c Release 12 | displayName: 'dotnet build' 13 | 14 | - script: | 15 | if "%BUILD_SOURCEBRANCH:~0,10%" == "refs/tags/" ( 16 | set releaseTag=%BUILD_SOURCEBRANCH:~10% 17 | ) else ( 18 | set releaseTag=CI 19 | ) 20 | echo ##vso[task.setvariable variable=GITHUB_RELEASE_TAG]%releaseTag% 21 | displayName: Set GITHUB_RELEASE env variables 22 | 23 | - task: NuGetCommand@2 24 | condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/') 25 | inputs: 26 | command: push 27 | nuGetFeedType: external 28 | publishFeedCredentials: 'FastDFSNetCoreNuget' 29 | packagesToPush: '$(Build.Repository.LocalPath)/src/FastDFS/bin/Release/*.nupkg' 30 | displayName: Upload NuGet 31 | 32 | - task: GitHubRelease@0 33 | condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/') 34 | inputs: 35 | gitHubConnection: FastDFSNetCore 36 | repositoryName: caozhiyuan/FastDFSNetCore 37 | action: edit 38 | tag: $(GITHUB_RELEASE_TAG) 39 | addChangeLog: true 40 | displayName: GitHub release 41 | -------------------------------------------------------------------------------- /src/FastDFS.Test/FDFSHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using FastDFS.Client; 7 | 8 | namespace FastDFS.Test 9 | { 10 | public class FDFSConfig 11 | { 12 | public string TackerIp { get; set; } 13 | 14 | public string TackerPort { get; set; } 15 | 16 | public string StorageGroup { get; set; } 17 | 18 | public string StorageServerLink { get; set; } 19 | } 20 | 21 | public static class FDFSHelper 22 | { 23 | /// 24 | /// GetFastDfsConfig 25 | /// 26 | /// 27 | private static Task GetFastDfsConfig() 28 | { 29 | //mock 30 | //get from app.config or zk 31 | return Task.FromResult(new FDFSConfig 32 | { 33 | TackerIp = "", 34 | TackerPort = "23000", 35 | StorageGroup = "", 36 | StorageServerLink = "" 37 | }); 38 | } 39 | 40 | /// 41 | /// 静态锁 42 | /// 43 | private static readonly object InitializeLock = new object(); 44 | 45 | /// 46 | /// 上传图片 47 | /// 48 | /// 比特数组 49 | /// 图片类型 50 | /// 图片地址 51 | public static async Task FastDFSUploadFile(byte[] contentBytes, string imageType) 52 | { 53 | if (contentBytes == null || contentBytes.Length == 0) 54 | throw new ArgumentNullException("contentBytes"); 55 | 56 | if (string.IsNullOrEmpty(imageType)) 57 | throw new ArgumentNullException("imageType"); 58 | 59 | var config = await GetFastDfsConfig(); 60 | 61 | EnsureConnectionInitialize(config); 62 | 63 | var group = config.StorageGroup; 64 | var storageNode = await FastDFSClient.GetStorageNodeAsync(group); 65 | string paths = await FastDFSClient.UploadFileAsync(storageNode, contentBytes, imageType); 66 | 67 | StringBuilder resultImageUrl = new StringBuilder(); 68 | var storageLink = config.StorageServerLink; 69 | resultImageUrl.Append(storageLink); 70 | resultImageUrl.Append(paths); 71 | 72 | return resultImageUrl.ToString(); 73 | } 74 | 75 | /// 76 | /// 是否已经初始化 77 | /// 78 | private static bool _isInitialize; 79 | 80 | /// 81 | /// 初始Connection 82 | /// 83 | private static void EnsureConnectionInitialize(FDFSConfig config) 84 | { 85 | if (!_isInitialize) 86 | { 87 | lock (InitializeLock) 88 | { 89 | if (!_isInitialize) 90 | { 91 | var trackerIp = config.TackerIp; 92 | int trackerPort = Convert.ToInt32(config.TackerPort); 93 | List trackerIPs = new List(); 94 | IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(trackerIp), trackerPort); 95 | trackerIPs.Add(endPoint); 96 | ConnectionManager.Initialize(trackerIPs); 97 | _isInitialize = true; 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/FastDFS.Test/FastDFS.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Always 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/FastDFS.Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using FastDFS.Client; 12 | 13 | namespace FastDFS.Test 14 | { 15 | internal class Program 16 | { 17 | const string StorageLink = "http://192.168.197.128/group1/"; 18 | private static readonly HttpClient HttpClient = new HttpClient(); 19 | 20 | static void Main(string[] args) 21 | { 22 | List pEndPoints = new List() 23 | { 24 | new IPEndPoint(IPAddress.Parse("192.168.197.128"), 22122) 25 | }; 26 | ConnectionManager.Initialize(pEndPoints); 27 | 28 | while (true) 29 | { 30 | Stopwatch sw = new Stopwatch(); 31 | sw.Start(); 32 | 33 | AsyncTest().Wait(); 34 | 35 | sw.Stop(); 36 | Console.WriteLine("AsyncTest " + sw.ElapsedMilliseconds); 37 | 38 | Console.ReadKey(); 39 | 40 | sw.Start(); 41 | 42 | SyncTest(); 43 | 44 | sw.Stop(); 45 | 46 | Console.WriteLine("SyncTest " + sw.ElapsedMilliseconds); 47 | 48 | Console.ReadKey(); 49 | 50 | UploadAppendFile().Wait(); 51 | 52 | DownLoadFile().Wait(); 53 | 54 | Console.ReadKey(); 55 | 56 | ParallelTest(); 57 | 58 | Console.ReadKey(); 59 | 60 | sw.Start(); 61 | 62 | BigFileUploadDownLoad().Wait(); 63 | 64 | sw.Stop(); 65 | Console.WriteLine("BigFileUploadDownLoad 100M " + sw.ElapsedMilliseconds); 66 | 67 | sw.Start(); 68 | 69 | BigFileAppendUploadDownLoad().Wait(); 70 | 71 | sw.Stop(); 72 | Console.WriteLine("BigFileAppendUploadDownLoad 100M " + sw.ElapsedMilliseconds); 73 | 74 | Console.ReadKey(); 75 | } 76 | } 77 | 78 | /// 79 | /// ParallelTest 80 | /// 81 | /// 82 | private static void ParallelTest() 83 | { 84 | Stopwatch sw = new Stopwatch(); 85 | sw.Start(); 86 | 87 | var by = GetFileBytes("testimage/1.jpg"); 88 | const int c = 500; 89 | CountdownEvent k = new CountdownEvent(c); 90 | Parallel.For(0, c, (i) => 91 | { 92 | var task = UploadAsync2(StorageLink, by); 93 | task.ContinueWith(n => 94 | { 95 | if (n.IsFaulted) 96 | { 97 | Console.Write("E"); 98 | } 99 | k.Signal(1); 100 | }); 101 | }); 102 | 103 | k.Wait(); 104 | sw.Stop(); 105 | Console.WriteLine(sw.ElapsedMilliseconds); 106 | } 107 | 108 | /// 109 | /// UploadAsync2 110 | /// 111 | /// 112 | /// 113 | /// 114 | private static async Task UploadAsync2(string storageLink, byte[] fileBytes) 115 | { 116 | StorageNode storageNode = await FastDFSClient.GetStorageNodeAsync("group1"); 117 | var str = await FastDFSClient.UploadFileAsync(storageNode, fileBytes, "jpg"); 118 | Console.WriteLine(storageLink + str); 119 | 120 | await FastDFSClient.RemoveFileAsync("group1", str); 121 | Console.WriteLine("FastDFSClient.RemoveFile" + str); 122 | } 123 | 124 | /// 125 | /// UploadAsync 126 | /// 127 | /// 128 | /// 129 | private static async Task UploadAsync(string storageLink) 130 | { 131 | StorageNode storageNode = await FastDFSClient.GetStorageNodeAsync("group1"); 132 | string[] files = Directory.GetFiles("testimage", "*.jpg"); 133 | string[] strArrays = files; 134 | for (int i = 0; i < strArrays.Length; i++) 135 | { 136 | string str1 = strArrays[i]; 137 | var numArray = GetFileBytes(str1); 138 | var str = await FastDFSClient.UploadFileAsync(storageNode, numArray, "jpg"); 139 | Console.WriteLine(storageLink + str); 140 | await FastDFSClient.RemoveFileAsync("group1", str); 141 | Console.WriteLine("FastDFSClient.RemoveFile" + str); 142 | } 143 | } 144 | 145 | private static readonly object Locker = new object(); 146 | 147 | /// 148 | /// GetFileBytes 149 | /// 150 | /// 151 | /// 152 | private static byte[] GetFileBytes(string str1) 153 | { 154 | lock (Locker) 155 | { 156 | var fileStream = new FileStream(str1, FileMode.Open); 157 | var binaryReader = new BinaryReader(fileStream); 158 | byte[] numArray; 159 | try 160 | { 161 | numArray = binaryReader.ReadBytes((int)fileStream.Length); 162 | } 163 | finally 164 | { 165 | binaryReader.Dispose(); 166 | } 167 | return numArray; 168 | } 169 | } 170 | 171 | /// 172 | /// AsyncTest 173 | /// 174 | /// 175 | private static async Task AsyncTest() 176 | { 177 | await UploadAsync(StorageLink); 178 | } 179 | 180 | /// 181 | /// SyncTest 182 | /// 183 | /// 184 | private static void SyncTest() 185 | { 186 | StorageNode storageNode = FastDFSClient.GetStorageNodeAsync("group1").GetAwaiter().GetResult(); 187 | string[] files = Directory.GetFiles("testimage", "*.jpg"); 188 | string[] strArrays = files; 189 | for (int i = 0; i < strArrays.Length; i++) 190 | { 191 | string str1 = strArrays[i]; 192 | var fileStream = new FileStream(str1, FileMode.Open); 193 | var binaryReader = new BinaryReader(fileStream); 194 | byte[] numArray; 195 | try 196 | { 197 | numArray = binaryReader.ReadBytes((int)fileStream.Length); 198 | } 199 | finally 200 | { 201 | binaryReader.Dispose(); 202 | } 203 | var str = FastDFSClient.UploadFileAsync(storageNode, numArray, "jpg").GetAwaiter().GetResult(); 204 | Console.WriteLine(StorageLink + str); 205 | FastDFSClient.RemoveFileAsync("group1", str).GetAwaiter().GetResult(); ; 206 | Console.WriteLine("FastDFSClient.RemoveFile" + str); 207 | } 208 | } 209 | 210 | private static async Task UploadAppendFile() 211 | { 212 | var testBytes = Encoding.UTF8.GetBytes("123456789"); 213 | StorageNode storageNode = await FastDFSClient.GetStorageNodeAsync("group1"); 214 | var filename = await FastDFSClient.UploadAppenderFileAsync(storageNode, testBytes.Take(6).ToArray(), ""); 215 | FDFSFileInfo fileInfo = await FastDFSClient.GetFileInfoAsync(storageNode, filename); 216 | if (fileInfo == null) 217 | { 218 | Console.WriteLine($"GetFileInfoAsync Fail, path: {filename}"); 219 | return; 220 | } 221 | 222 | Console.WriteLine("FileName:{0}", filename); 223 | Console.WriteLine("FileSize:{0}", fileInfo.FileSize); 224 | Console.WriteLine("CreateTime:{0}", fileInfo.CreateTime); 225 | Console.WriteLine("Crc32:{0}", fileInfo.Crc32); 226 | 227 | var appendBytes = testBytes.Skip((int) fileInfo.FileSize).ToArray(); 228 | await FastDFSClient.AppendFileAsync("group1", filename, appendBytes); 229 | 230 | var test = await HttpClient.GetByteArrayAsync(StorageLink + filename); 231 | if (Encoding.UTF8.GetString(test) == Encoding.UTF8.GetString(testBytes)) 232 | { 233 | Console.WriteLine($"UploadAppendFile Success"); 234 | } 235 | else 236 | { 237 | throw new ApplicationException($"UploadAppendFile Fail : Bytes Diff "); 238 | } 239 | await FastDFSClient.RemoveFileAsync("group1", filename); 240 | 241 | } 242 | 243 | private static async Task DownLoadFile() 244 | { 245 | var testBytes = Encoding.UTF8.GetBytes("12345678911118888888888888888881111111111"); 246 | StorageNode storageNode = await FastDFSClient.GetStorageNodeAsync("group1"); 247 | var filename = await FastDFSClient.UploadFileAsync(storageNode, testBytes, "txt"); 248 | 249 | var bytes = await FastDFSClient.DownloadFileAsync(storageNode, filename); 250 | if (bytes == null) 251 | { 252 | Console.WriteLine($"DownLoadFile Fail : Bytes null "); 253 | } 254 | if (Encoding.UTF8.GetString(bytes) == Encoding.UTF8.GetString(testBytes)) 255 | { 256 | Console.WriteLine($"DownLoadFile Success"); 257 | } 258 | else 259 | { 260 | throw new ApplicationException("DownLoadFile Fail : Bytes Diff"); 261 | } 262 | } 263 | 264 | protected static async Task BigFileUploadDownLoad() 265 | { 266 | var temp = Enumerable.Repeat((byte) 99, 1024 * 1024 * 100); 267 | var testBytes = temp.ToArray(); 268 | StorageNode storageNode = await FastDFSClient.GetStorageNodeAsync("group1"); 269 | 270 | var filename = await FastDFSClient.UploadFileAsync(storageNode, testBytes, "txt"); 271 | 272 | using (var fileStream = File.OpenWrite("c:\\fastdfs_test.txt")) 273 | { 274 | await FastDFSClient.DownloadFileAsync(storageNode, filename, new StreamDownloadCallback(fileStream)); 275 | } 276 | 277 | await FastDFSClient.RemoveFileAsync("group1", filename); 278 | 279 | temp = null; 280 | testBytes = null; 281 | } 282 | 283 | protected static async Task BigFileAppendUploadDownLoad() 284 | { 285 | var temp = Enumerable.Repeat((byte)99, 1024 * 1024 * 100); 286 | var testBytes = temp.ToArray(); 287 | StorageNode storageNode = await FastDFSClient.GetStorageNodeAsync("group1"); 288 | 289 | 290 | var filename = await FastDFSClient.UploadAppenderFileAsync(storageNode, testBytes.Take(1024 * 1024 * 2).ToArray(), "txt"); 291 | 292 | for (int i = 0; i < 49; i++) 293 | { 294 | FDFSFileInfo fileInfo = await FastDFSClient.GetFileInfoAsync(storageNode, filename); 295 | var appendBytes = testBytes.Skip((int)fileInfo.FileSize).Take(1024 * 1024 * 2).ToArray(); 296 | await FastDFSClient.AppendFileAsync("group1", filename, appendBytes); 297 | } 298 | 299 | using (var fileStream = File.OpenWrite("c:\\fastdfs_test.txt")) 300 | { 301 | for (int i = 0; i < 50; i++) 302 | { 303 | var buffer = await FastDFSClient.DownloadFileAsync(storageNode, filename, 304 | 1024 * 1024 * 2 * i, 305 | 1024 * 1024 * 2); 306 | fileStream.Write(buffer, 0, buffer.Length); 307 | } 308 | } 309 | 310 | await FastDFSClient.RemoveFileAsync("group1", filename); 311 | 312 | temp = null; 313 | testBytes = null; 314 | } 315 | } 316 | } -------------------------------------------------------------------------------- /src/FastDFS.Test/testimage/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caozhiyuan/FastDFSNetCore/d54baf4cbdd2ec5a0cc47ff981dd1d8d315a3035/src/FastDFS.Test/testimage/0.jpg -------------------------------------------------------------------------------- /src/FastDFS.Test/testimage/002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caozhiyuan/FastDFSNetCore/d54baf4cbdd2ec5a0cc47ff981dd1d8d315a3035/src/FastDFS.Test/testimage/002.jpg -------------------------------------------------------------------------------- /src/FastDFS.Test/testimage/004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caozhiyuan/FastDFSNetCore/d54baf4cbdd2ec5a0cc47ff981dd1d8d315a3035/src/FastDFS.Test/testimage/004.jpg -------------------------------------------------------------------------------- /src/FastDFS.Test/testimage/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caozhiyuan/FastDFSNetCore/d54baf4cbdd2ec5a0cc47ff981dd1d8d315a3035/src/FastDFS.Test/testimage/1.jpg -------------------------------------------------------------------------------- /src/FastDFS.Test/testimage/2c8f484465db50d6dc9c28f301c59172_big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caozhiyuan/FastDFSNetCore/d54baf4cbdd2ec5a0cc47ff981dd1d8d315a3035/src/FastDFS.Test/testimage/2c8f484465db50d6dc9c28f301c59172_big.jpg -------------------------------------------------------------------------------- /src/FastDFS/Client/APPEND_FILE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace FastDFS.Client 6 | { 7 | internal class APPEND_FILE : FDFSRequest 8 | { 9 | public static readonly APPEND_FILE Instance = new APPEND_FILE(); 10 | 11 | private APPEND_FILE() 12 | { 13 | } 14 | 15 | public override FDFSRequest GetRequest(params object[] paramList) 16 | { 17 | if (paramList.Length != 3) 18 | throw new FDFSException("param count is wrong"); 19 | 20 | var endPoint = (IPEndPoint) paramList[0]; 21 | 22 | var fileName = (string) paramList[1]; 23 | var contentStream = (Stream) paramList[2]; 24 | 25 | var appendFile = new APPEND_FILE 26 | { 27 | ConnectionType = FDFSConnectionType.StorageConnection, 28 | EndPoint = endPoint 29 | }; 30 | 31 | var fileNameByteCount = Util.StringByteCount(fileName); 32 | int bodyBufferLen = 16 + fileNameByteCount; 33 | appendFile.SetBodyBuffer(bodyBufferLen); 34 | 35 | int offset = 0; 36 | 37 | Util.LongToBuffer(fileNameByteCount, appendFile.BodyBuffer, offset); 38 | offset += 8; 39 | 40 | Util.LongToBuffer(contentStream.Length, appendFile.BodyBuffer, offset); 41 | offset += 8; 42 | 43 | Util.StringToByte(fileName, appendFile.BodyBuffer, offset, fileNameByteCount); 44 | 45 | appendFile.BodyStream = contentStream; 46 | 47 | long length = bodyBufferLen + contentStream.Length; 48 | appendFile.Header = new FDFSHeader(length, FDFSConstants.STORAGE_PROTO_CMD_APPEND_FILE, 0); 49 | return appendFile; 50 | } 51 | 52 | public class Response : IFDFSResponse 53 | { 54 | public void ParseBuffer(byte[] responseByte, int length) 55 | { 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/FastDFS/Client/Connection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Threading.Tasks; 8 | 9 | namespace FastDFS.Client 10 | { 11 | internal class Connection 12 | { 13 | private const int DEFAULT_BUFFER_SIZE = 1024 * 64; 14 | 15 | private Pool _pool; 16 | 17 | private DateTime _lastUseTime; 18 | 19 | private bool _inUse = false; 20 | 21 | public bool InUse 22 | { 23 | get => this._inUse; 24 | set => this._inUse = value; 25 | } 26 | 27 | public Pool Pool 28 | { 29 | get => this._pool; 30 | set => this._pool = value; 31 | } 32 | 33 | private readonly EndPoint _endPoint; 34 | 35 | private Socket Socket { get; set; } 36 | 37 | public Connection(EndPoint endPoint) 38 | { 39 | _endPoint = endPoint; 40 | } 41 | 42 | internal void ReUse() 43 | { 44 | this._pool.CloseConnection(this); 45 | } 46 | 47 | internal async Task OpenAsync() 48 | { 49 | if (this._inUse) 50 | { 51 | throw new FDFSException("the connection is already in user"); 52 | } 53 | 54 | if (_lastUseTime != default(DateTime) && IdleTotalSeconds > FDFSConfig.ConnectionLifeTime) 55 | { 56 | await CloseSocketAsync(); 57 | } 58 | 59 | this._inUse = true; 60 | this._lastUseTime = DateTime.Now; 61 | 62 | if (Socket == null || !Socket.Connected) 63 | { 64 | Socket = NewSocket(); 65 | await Socket.ConnectExAsync(_endPoint); 66 | } 67 | } 68 | 69 | private double IdleTotalSeconds 70 | { 71 | get { return (DateTime.Now - _lastUseTime).TotalSeconds; } 72 | } 73 | 74 | private Socket NewSocket() 75 | { 76 | var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 77 | socket.LingerState = new LingerOption(false, 0); 78 | socket.NoDelay = true; 79 | return socket; 80 | } 81 | 82 | internal async Task SendExAsync(IList> buffers) 83 | { 84 | try 85 | { 86 | return await Socket.SendExAsync(buffers); 87 | } 88 | catch (Exception) 89 | { 90 | await CloseSocketAsync(); 91 | throw; 92 | } 93 | } 94 | 95 | internal async Task SendExAsync(Stream stream) 96 | { 97 | var buffer = ArrayPool.Shared.Rent(DEFAULT_BUFFER_SIZE); 98 | try 99 | { 100 | int read; 101 | while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0) 102 | { 103 | await Socket.SendExAsync(buffer, 0, read); 104 | } 105 | } 106 | catch (Exception) 107 | { 108 | await CloseSocketAsync(); 109 | throw; 110 | } 111 | finally 112 | { 113 | ArrayPool.Shared.Return(buffer); 114 | } 115 | } 116 | 117 | internal async Task ReceiveExAsync(byte[] buffer, int length) 118 | { 119 | var sent = await Socket.ReceiveExAsync(buffer, length); 120 | if (sent == 0) 121 | { 122 | await CloseSocketAsync(); 123 | } 124 | 125 | return sent; 126 | } 127 | 128 | private async Task CloseSocketAsync() 129 | { 130 | var header = new FDFSHeader(0L, FDFSConstants.FDFS_PROTO_CMD_QUIT, 0); 131 | try 132 | { 133 | var headerArray = header.GetBuffer(); 134 | await Socket.SendExAsync(new List> 135 | { 136 | headerArray 137 | }); 138 | Socket.Close(); 139 | Socket.Dispose(); 140 | } 141 | catch 142 | { 143 | // ignored 144 | } 145 | finally 146 | { 147 | header.Dispose(); 148 | } 149 | 150 | Socket = null; 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/ConnectionManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastDFS.Client 8 | { 9 | public class ConnectionManager 10 | { 11 | private static List _listTrackers = new List(); 12 | private static readonly Dictionary TrackerPools = new Dictionary(); 13 | private static readonly ConcurrentDictionary StorePools = new ConcurrentDictionary(); 14 | 15 | internal static Task GetStorageConnectionAsync(EndPoint endPoint) 16 | { 17 | return StorePools.GetOrAdd(endPoint, (ep) => new Pool(ep, FDFSConfig.StorageMaxConnection)).GetConnectionAsync(); 18 | } 19 | 20 | internal static Task GetTrackerConnectionAsync() 21 | { 22 | int num = new Random().Next(TrackerPools.Count); 23 | Pool pool = TrackerPools[_listTrackers[num]]; 24 | return pool.GetConnectionAsync(); 25 | } 26 | 27 | public static bool Initialize(IEnumerable trackers) 28 | { 29 | foreach (EndPoint point in trackers) 30 | { 31 | if (!TrackerPools.ContainsKey(point)) 32 | { 33 | TrackerPools.Add(point, new Pool(point, FDFSConfig.TrackerMaxConnection)); 34 | _listTrackers.Add(point); 35 | } 36 | } 37 | return true; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/DELETE_FILE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace FastDFS.Client 5 | { 6 | internal class DELETE_FILE : FDFSRequest 7 | { 8 | public static readonly DELETE_FILE Instance = new DELETE_FILE(); 9 | 10 | private DELETE_FILE() 11 | { 12 | } 13 | 14 | public override FDFSRequest GetRequest(params object[] paramList) 15 | { 16 | if (paramList.Length != 3) 17 | { 18 | throw new FDFSException("param count is wrong"); 19 | } 20 | IPEndPoint endPoint = (IPEndPoint) paramList[0]; 21 | string groupName = (string) paramList[1]; 22 | string fileName = (string) paramList[2]; 23 | DELETE_FILE deleteFile = new DELETE_FILE 24 | { 25 | ConnectionType = FDFSConnectionType.StorageConnection, 26 | EndPoint = endPoint 27 | }; 28 | var groupNameByteCount = Util.StringByteCount(groupName); 29 | if (groupNameByteCount > 16) 30 | { 31 | throw new FDFSException("groupName is too long"); 32 | } 33 | var fileNameByteCount = Util.StringByteCount(fileName); 34 | int length = 16 + fileNameByteCount; 35 | deleteFile.SetBodyBuffer(length); 36 | Util.StringToByte(groupName, deleteFile.BodyBuffer, 0, groupNameByteCount); 37 | if (groupNameByteCount < 16) 38 | { 39 | for (var i = groupNameByteCount; i < 16; i++) 40 | { 41 | deleteFile.BodyBuffer[i] = 0; 42 | } 43 | } 44 | 45 | Util.StringToByte(fileName, deleteFile.BodyBuffer, 16, fileNameByteCount); 46 | deleteFile.Header = new FDFSHeader(length, FDFSConstants.STORAGE_PROTO_CMD_DELETE_FILE, 0); 47 | return deleteFile; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/DOWNLOAD_FILE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | namespace FastDFS.Client 7 | { 8 | internal class DOWNLOAD_FILE : FDFSRequest 9 | { 10 | private const int DEFAULT_BUFFER_SIZE = 1024 * 256; 11 | 12 | public IDownloadCallback Callback { get; set; } 13 | 14 | public static readonly DOWNLOAD_FILE Instance = new DOWNLOAD_FILE(); 15 | 16 | private DOWNLOAD_FILE() 17 | { 18 | 19 | } 20 | 21 | public override FDFSRequest GetRequest(params object[] paramList) 22 | { 23 | if (paramList.Length != 4) 24 | throw new FDFSException("param count is wrong"); 25 | 26 | var storageNode = (StorageNode)paramList[0]; 27 | var fileName = (string)paramList[1]; 28 | var offsetInfo = (Tuple)paramList[2]; 29 | 30 | var downloadFile = new DOWNLOAD_FILE 31 | { 32 | ConnectionType = FDFSConnectionType.StorageConnection, 33 | EndPoint = storageNode.EndPoint, 34 | Callback = (IDownloadCallback) paramList[3] 35 | }; 36 | 37 | var groupNameByteCount = Util.StringByteCount(storageNode.GroupName); 38 | if (groupNameByteCount > 16) 39 | { 40 | throw new FDFSException("groupName is too long"); 41 | } 42 | 43 | var fileNameByteCount = Util.StringByteCount(fileName); 44 | int length = 16 + 16 + fileNameByteCount; 45 | 46 | downloadFile.SetBodyBuffer(length); 47 | 48 | int offset = 0; 49 | 50 | Util.LongToBuffer(offsetInfo.Item1, downloadFile.BodyBuffer, offset); 51 | offset += 8; 52 | 53 | Util.LongToBuffer(offsetInfo.Item2, downloadFile.BodyBuffer, offset); 54 | offset += 8; 55 | 56 | Util.StringToByte(storageNode.GroupName, downloadFile.BodyBuffer, offset, groupNameByteCount); 57 | if (groupNameByteCount < 16) 58 | { 59 | for (var i = offset + groupNameByteCount; i < offset + 16; i++) 60 | { 61 | downloadFile.BodyBuffer[i] = 0; 62 | } 63 | } 64 | offset += 16; 65 | 66 | Util.StringToByte(fileName, downloadFile.BodyBuffer, offset, fileNameByteCount); 67 | 68 | downloadFile.Header = new FDFSHeader(length, FDFSConstants.STORAGE_PROTO_CMD_DOWNLOAD_FILE, 0); 69 | return downloadFile; 70 | } 71 | 72 | protected override async Task ParseResponseInfo(FDFSHeader responseHeader) 73 | { 74 | var buff = ArrayPool.Shared.Rent(DEFAULT_BUFFER_SIZE); 75 | try 76 | { 77 | var remainBytes = responseHeader.Length; 78 | while (remainBytes > 0) 79 | { 80 | var bytes = await _connection.ReceiveExAsync(buff, remainBytes > buff.Length ? buff.Length : (int)remainBytes); 81 | int result; 82 | if ((result = await Callback.ReceiveAsync(responseHeader.Length, buff, bytes)) != 0) 83 | { 84 | throw new FDFSStatusException(responseHeader.Status, $"Callback Receive Error:{result}"); 85 | } 86 | remainBytes -= bytes; 87 | } 88 | return new T(); 89 | } 90 | finally 91 | { 92 | ArrayPool.Shared.Return(buff); 93 | responseHeader.Dispose(); 94 | } 95 | } 96 | 97 | public class Response : IFDFSResponse 98 | { 99 | public void ParseBuffer(byte[] responseByte, int length) 100 | { 101 | } 102 | } 103 | } 104 | 105 | public interface IDownloadCallback 106 | { 107 | Task ReceiveAsync(long fileSize, byte[] data, int bytes); 108 | } 109 | 110 | public class StreamDownloadCallback : IDownloadCallback 111 | { 112 | private readonly Stream _stream; 113 | 114 | public StreamDownloadCallback(Stream stream) 115 | { 116 | _stream = stream; 117 | } 118 | 119 | public async Task ReceiveAsync(long fileSize, byte[] data, int bytes) 120 | { 121 | await _stream.WriteAsync(data, 0, bytes); 122 | return 0; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/FastDFS/Client/FDFSConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace FastDFS.Client 4 | { 5 | internal class FDFSConfig 6 | { 7 | public static readonly Encoding Charset = Encoding.UTF8; 8 | public const int ConnectionLifeTime = 1800; 9 | public const int GetConnectionTimeout = 5; 10 | public const int StorageMaxConnection = 120; 11 | public const int TrackerMaxConnection = 60; 12 | } 13 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/FDFSConstants.cs: -------------------------------------------------------------------------------- 1 | namespace FastDFS.Client 2 | { 3 | internal class FDFSConstants 4 | { 5 | public const byte FDFS_PROTO_CMD_QUIT = 82; 6 | public const byte TRACKER_PROTO_CMD_SERVER_LIST_GROUP = 91; 7 | public const byte TRACKER_PROTO_CMD_SERVER_LIST_STORAGE = 92; 8 | public const byte TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE = 93; 9 | public const byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE = 101; 10 | public const byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE = 102; 11 | public const byte TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE = 103; 12 | public const byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE = 104; 13 | public const byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL = 105; 14 | public const byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL = 106; 15 | public const byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL = 107; 16 | public const byte TRACKER_PROTO_CMD_RESP = 100; 17 | public const byte FDFS_PROTO_CMD_ACTIVE_TEST = 111; 18 | public const byte STORAGE_PROTO_CMD_UPLOAD_FILE = 11; 19 | public const byte STORAGE_PROTO_CMD_DELETE_FILE = 12; 20 | public const byte STORAGE_PROTO_CMD_SET_METADATA = 13; 21 | public const byte STORAGE_PROTO_CMD_DOWNLOAD_FILE = 14; 22 | public const byte STORAGE_PROTO_CMD_GET_METADATA = 15; 23 | public const byte STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE = 21; 24 | public const byte STORAGE_PROTO_CMD_QUERY_FILE_INFO = 22; 25 | public const byte STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE = 23; //create appender file 26 | public const byte STORAGE_PROTO_CMD_APPEND_FILE = 24; //append file 27 | public const byte STORAGE_PROTO_CMD_MODIFY_FILE = 34; //modify appender file 28 | public const byte STORAGE_PROTO_CMD_TRUNCATE_FILE = 36; //truncate appender file 29 | public const byte STORAGE_PROTO_CMD_RESP = TRACKER_PROTO_CMD_RESP; 30 | 31 | public enum ErrorCode 32 | { 33 | ERR_NO_ENOENT = 2, 34 | ERR_NO_EIO = 5, 35 | ERR_NO_EBUSY = 16, 36 | ERR_NO_EINVAL = 22, 37 | ERR_NO_ENOSPC = 28, 38 | ECONNREFUSED = 61, 39 | ERR_NO_EALREADY = 114 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/FastDFS/Client/FDFSException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FastDFS.Client 4 | { 5 | public class FDFSException : Exception 6 | { 7 | public FDFSException(string msg) : base(msg) 8 | { 9 | 10 | } 11 | } 12 | 13 | public class FDFSStatusException : Exception 14 | { 15 | public int Status { get; set; } 16 | 17 | public FDFSStatusException(int status, string msg) : base(msg) 18 | { 19 | Status = status; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/FDFSHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | 4 | namespace FastDFS.Client 5 | { 6 | internal class FDFSHeader:IDisposable 7 | { 8 | private byte[] _headerBuffer; 9 | 10 | public byte Command { get; set; } 11 | 12 | public long Length { get; set; } 13 | 14 | public byte Status { get; set; } 15 | 16 | public FDFSHeader(long length, byte command, byte status) 17 | { 18 | this.Length = length; 19 | this.Command = command; 20 | this.Status = status; 21 | } 22 | 23 | public ArraySegment GetBuffer() 24 | { 25 | _headerBuffer = _headerBuffer ?? ArrayPool.Shared.Rent(10); 26 | Util.LongToBuffer(this.Length, _headerBuffer, 0); 27 | _headerBuffer[8] = this.Command; 28 | _headerBuffer[9] = this.Status; 29 | return new ArraySegment(_headerBuffer, 0, 10); 30 | } 31 | 32 | public void Dispose() 33 | { 34 | if (_headerBuffer != null) 35 | { 36 | ArrayPool.Shared.Return(_headerBuffer); 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/FDFSRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | 8 | namespace FastDFS.Client 9 | { 10 | internal class FDFSRequest 11 | { 12 | protected enum FDFSConnectionType 13 | { 14 | TrackerConnection = 0, 15 | StorageConnection = 1 16 | } 17 | 18 | protected FDFSConnectionType ConnectionType { get; set; } 19 | 20 | protected Connection _connection; 21 | 22 | protected IPEndPoint EndPoint { get; set; } 23 | 24 | protected FDFSHeader Header { get; set; } 25 | 26 | protected byte[] BodyBuffer { get; set; } 27 | 28 | protected int BodyBufferLength { get; set; } 29 | 30 | protected Stream BodyStream { get; set; } 31 | 32 | protected FDFSRequest() 33 | { 34 | ConnectionType = FDFSConnectionType.TrackerConnection; 35 | } 36 | 37 | protected void SetBodyBuffer(int length) 38 | { 39 | BodyBuffer = ArrayPool.Shared.Rent(length); 40 | BodyBufferLength = length; 41 | } 42 | 43 | public virtual FDFSRequest GetRequest(params object[] paramList) 44 | { 45 | throw new NotImplementedException(); 46 | } 47 | 48 | public async Task GetResponseAsync() where T: IFDFSResponse, new () 49 | { 50 | try 51 | { 52 | if (ConnectionType == FDFSConnectionType.TrackerConnection) 53 | { 54 | _connection = await ConnectionManager.GetTrackerConnectionAsync(); 55 | } 56 | else 57 | { 58 | _connection = await ConnectionManager.GetStorageConnectionAsync(EndPoint); 59 | } 60 | await _connection.OpenAsync(); 61 | 62 | var headerBuffer = Header.GetBuffer(); 63 | var buffers = new List>(2) 64 | { 65 | headerBuffer, 66 | new ArraySegment(BodyBuffer, 0, BodyBufferLength) 67 | }; 68 | await _connection.SendExAsync(buffers); 69 | 70 | if (BodyStream != null) 71 | { 72 | await _connection.SendExAsync(BodyStream); 73 | } 74 | 75 | var responseHeader = await GetResponseHeaderInfo(headerBuffer); 76 | 77 | return await GetResponseInfo(responseHeader); 78 | } 79 | finally 80 | { 81 | if (BodyBuffer != null) 82 | { 83 | ArrayPool.Shared.Return(BodyBuffer); 84 | } 85 | 86 | Header?.Dispose(); 87 | 88 | _connection?.ReUse(); 89 | } 90 | } 91 | 92 | private async Task GetResponseHeaderInfo(ArraySegment arraySegment) where T : IFDFSResponse, new() 93 | { 94 | var headerArray = arraySegment.Array; 95 | if (headerArray == null) 96 | { 97 | throw new ArgumentNullException(nameof(arraySegment.Array)); 98 | } 99 | 100 | if (await _connection.ReceiveExAsync(headerArray, arraySegment.Count) == 0) 101 | { 102 | throw new FDFSException("Init Header Exception : Can't Read Stream"); 103 | } 104 | 105 | var length = Util.BufferToLong(headerArray, 0); 106 | var command = headerArray[8]; 107 | var status = headerArray[9]; 108 | return new FDFSHeader(length, command, status); 109 | } 110 | 111 | private async Task GetResponseInfo(FDFSHeader responseHeader) where T : IFDFSResponse, new() 112 | { 113 | if (responseHeader.Status != 0) 114 | { 115 | if (Header.Command == FDFSConstants.STORAGE_PROTO_CMD_QUERY_FILE_INFO) 116 | { 117 | return default(T); 118 | } 119 | throw new FDFSStatusException(responseHeader.Status, $"Get Response Error, Error Code:{(FDFSConstants.ErrorCode)responseHeader.Status}"); 120 | } 121 | 122 | if (responseHeader.Length <= 0) 123 | { 124 | return default(T); 125 | } 126 | 127 | return await ParseResponseInfo(responseHeader); 128 | } 129 | 130 | protected virtual async Task ParseResponseInfo(FDFSHeader responseHeader) where T : IFDFSResponse, new() 131 | { 132 | int resLen = (int) responseHeader.Length; 133 | var resBuffer = ArrayPool.Shared.Rent(resLen); 134 | try 135 | { 136 | await _connection.ReceiveExAsync(resBuffer, resLen); 137 | var response = new T(); 138 | response.ParseBuffer(resBuffer, resLen); 139 | return response; 140 | } 141 | finally 142 | { 143 | ArrayPool.Shared.Return(resBuffer); 144 | responseHeader.Dispose(); 145 | } 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/FDFSResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FastDFS.Client 2 | { 3 | internal interface IFDFSResponse 4 | { 5 | void ParseBuffer(byte[] responseBytes, int length); 6 | } 7 | 8 | internal class EmptyFDFSResponse: IFDFSResponse 9 | { 10 | public void ParseBuffer(byte[] responseBytes, int length) 11 | { 12 | 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/FastDFS/Client/FastDFSClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Threading.Tasks; 5 | 6 | namespace FastDFS.Client 7 | { 8 | public class FastDFSClient 9 | { 10 | public static Task GetFileInfoAsync(StorageNode storageNode, string fileName) 11 | { 12 | return QUERY_FILE_INFO.Instance.GetRequest(storageNode.EndPoint, 13 | storageNode.GroupName, 14 | fileName) 15 | .GetResponseAsync(); 16 | } 17 | 18 | public static Task GetStorageNodeAsync() 19 | { 20 | return GetStorageNodeAsync(null); 21 | } 22 | 23 | public static async Task GetStorageNodeAsync(string groupName) 24 | { 25 | var request = string.IsNullOrWhiteSpace(groupName) ? 26 | QUERY_STORE_WITHOUT_GROUP_ONE.Instance.GetRequest() 27 | : QUERY_STORE_WITH_GROUP_ONE.Instance.GetRequest(groupName); 28 | var response = await request.GetResponseAsync(); 29 | 30 | var point = new IPEndPoint(IPAddress.Parse(response.IPStr), response.Port); 31 | return new StorageNode 32 | { 33 | GroupName = response.GroupName, 34 | EndPoint = point, 35 | StorePathIndex = response.StorePathIndex 36 | }; 37 | } 38 | 39 | public static async Task RemoveFileAsync(string groupName, string fileName) 40 | { 41 | var response = await QUERY_UPDATE.Instance 42 | .GetRequest(groupName, fileName) 43 | .GetResponseAsync(); 44 | 45 | var point = new IPEndPoint(IPAddress.Parse(response.IPStr), response.Port); 46 | await DELETE_FILE.Instance 47 | .GetRequest(point, groupName, fileName) 48 | .GetResponseAsync(); 49 | } 50 | 51 | public static async Task UploadFileAsync(StorageNode storageNode, Stream contentStream, string fileExt) 52 | { 53 | var response = await UPLOAD_FILE.Instance.GetRequest(storageNode, 54 | fileExt, 55 | contentStream) 56 | .GetResponseAsync(); 57 | return response.FileName; 58 | } 59 | 60 | public static async Task UploadFileAsync(StorageNode storageNode, byte[] contentByte, string fileExt) 61 | { 62 | using (var contentStream = new MemoryStream(contentByte, false)) 63 | { 64 | return await UploadFileAsync(storageNode, contentStream, fileExt); 65 | } 66 | } 67 | 68 | public static async Task UploadAppenderFileAsync(StorageNode storageNode, Stream contentStream, string fileExt) 69 | { 70 | var response = await UPLOAD_APPEND_FILE.Instance.GetRequest(storageNode, 71 | fileExt, 72 | contentStream) 73 | .GetResponseAsync(); 74 | return response.FileName; 75 | } 76 | 77 | public static async Task UploadAppenderFileAsync(StorageNode storageNode, byte[] contentByte, string fileExt) 78 | { 79 | using (var contentStream = new MemoryStream(contentByte, false)) 80 | { 81 | return await UploadAppenderFileAsync(storageNode,contentStream,fileExt); 82 | } 83 | } 84 | 85 | public static async Task AppendFileAsync(string groupName, string fileName, Stream contentStream) 86 | { 87 | var response = await QUERY_UPDATE.Instance 88 | .GetRequest(groupName, fileName) 89 | .GetResponseAsync(); 90 | 91 | var point = new IPEndPoint(IPAddress.Parse(response.IPStr), response.Port); 92 | 93 | await APPEND_FILE.Instance.GetRequest(point, 94 | fileName, 95 | contentStream) 96 | .GetResponseAsync(); 97 | } 98 | 99 | public static async Task AppendFileAsync(string groupName, string fileName, byte[] contentByte) 100 | { 101 | using (var contentStream = new MemoryStream(contentByte, false)) 102 | { 103 | await AppendFileAsync(groupName, fileName, contentStream); 104 | } 105 | } 106 | 107 | public static async Task DownloadFileAsync(StorageNode storageNode, string fileName, 108 | long offset = 0, 109 | long length = 0) 110 | { 111 | using (var memoryStream = new MemoryStream(length > 0 ? (int) length : 0)) 112 | { 113 | var downloadStream = new StreamDownloadCallback(memoryStream); 114 | await DownloadFileAsync(storageNode, fileName, downloadStream, offset, length); 115 | return memoryStream.ToArray(); 116 | } 117 | } 118 | 119 | public static async Task DownloadFileAsync(StorageNode storageNode, string fileName, 120 | IDownloadCallback downloadCallback, 121 | long offset = 0, 122 | long length = 0) 123 | { 124 | await DOWNLOAD_FILE.Instance 125 | .GetRequest(storageNode, 126 | fileName, 127 | Tuple.Create(offset, length), 128 | downloadCallback) 129 | .GetResponseAsync(); 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/Pool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace FastDFS.Client 7 | { 8 | internal class Pool 9 | { 10 | private readonly SemaphoreSlim _semaphoreSlim; 11 | private readonly EndPoint _endPoint; 12 | private readonly Stack _idle; 13 | private readonly List _inUse; 14 | private readonly int _maxConnection; 15 | private readonly object _locker = new object(); 16 | 17 | public Pool(EndPoint endPoint, int maxConnection) 18 | { 19 | this._semaphoreSlim = new SemaphoreSlim(maxConnection); 20 | this._inUse = new List(maxConnection); 21 | this._idle = new Stack(maxConnection); 22 | this._maxConnection = maxConnection; 23 | this._endPoint = endPoint; 24 | 25 | } 26 | 27 | public void CloseConnection(Connection conn) 28 | { 29 | conn.InUse = false; 30 | lock (_locker) 31 | { 32 | this._inUse.Remove(conn); 33 | } 34 | lock (_locker) 35 | { 36 | this._idle.Push(conn); 37 | } 38 | this._semaphoreSlim.Release(); 39 | } 40 | 41 | public async Task GetConnectionAsync() 42 | { 43 | int millisecondsTimeout = FDFSConfig.GetConnectionTimeout * 1000; 44 | while (true) 45 | { 46 | if (!await this._semaphoreSlim.WaitAsync(millisecondsTimeout)) 47 | { 48 | break; 49 | } 50 | 51 | var pooledConnection = this.GetPooledConnection(); 52 | if (pooledConnection != null) 53 | { 54 | return pooledConnection; 55 | } 56 | } 57 | throw new FDFSException("Get CanUse Connection Time Out"); 58 | } 59 | 60 | private Connection GetPooledConnection() 61 | { 62 | Connection item = null; 63 | lock (_locker) 64 | { 65 | if (this._idle.Count > 0) 66 | { 67 | item = this._idle.Pop(); 68 | } 69 | } 70 | lock (_locker) 71 | { 72 | if (this._inUse.Count == this._maxConnection) 73 | { 74 | return null; 75 | } 76 | if (item == null) 77 | { 78 | item = new Connection(_endPoint) 79 | { 80 | Pool = this 81 | }; 82 | } 83 | this._inUse.Add(item); 84 | } 85 | return item; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/QUERY_FILE_INFO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace FastDFS.Client 5 | { 6 | internal class QUERY_FILE_INFO : FDFSRequest 7 | { 8 | public static readonly QUERY_FILE_INFO Instance = new QUERY_FILE_INFO(); 9 | 10 | private QUERY_FILE_INFO() 11 | { 12 | } 13 | 14 | public override FDFSRequest GetRequest(params object[] paramList) 15 | { 16 | if ((int)paramList.Length != 3) 17 | { 18 | throw new FDFSException("param count is wrong"); 19 | } 20 | IPEndPoint pEndPoint = (IPEndPoint)paramList[0]; 21 | string groupName = (string)paramList[1]; 22 | string fileName = (string)paramList[2]; 23 | QUERY_FILE_INFO queryFileInfo = new QUERY_FILE_INFO 24 | { 25 | ConnectionType = FDFSConnectionType.StorageConnection, 26 | EndPoint = pEndPoint 27 | }; 28 | var groupNameByteCount = Util.StringByteCount(groupName); 29 | if (groupNameByteCount > 16) 30 | { 31 | throw new FDFSException("groupName is too long"); 32 | } 33 | var fileNameByteCount = Util.StringByteCount(fileName); 34 | int length = 16 + fileNameByteCount; 35 | queryFileInfo.SetBodyBuffer(length); 36 | Util.StringToByte(groupName, queryFileInfo.BodyBuffer, 0, groupNameByteCount); 37 | if (groupNameByteCount < 16) 38 | { 39 | for (var i = groupNameByteCount; i < 16; i++) 40 | { 41 | queryFileInfo.BodyBuffer[i] = 0; 42 | } 43 | } 44 | Util.StringToByte(fileName, queryFileInfo.BodyBuffer, 16, fileNameByteCount); 45 | queryFileInfo.Header = new FDFSHeader(length, FDFSConstants.STORAGE_PROTO_CMD_QUERY_FILE_INFO, 0); 46 | return queryFileInfo; 47 | } 48 | } 49 | 50 | public class FDFSFileInfo : IFDFSResponse 51 | { 52 | public long FileSize { get; private set; } 53 | 54 | public DateTime CreateTime { get; private set; } 55 | 56 | public long Crc32 { get; private set; } 57 | 58 | public void ParseBuffer(byte[] responseBytes, int length) 59 | { 60 | this.FileSize = Util.BufferToLong(responseBytes, 0); 61 | DateTime dateTime = new DateTime(1970, 1, 1); 62 | this.CreateTime = dateTime.AddSeconds(Util.BufferToLong(responseBytes, 8)).ToLocalTime(); 63 | this.Crc32 = Util.BufferToLong(responseBytes, 16); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/QUERY_STORE_WITHOUT_GROUP_ONE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FastDFS.Client 4 | { 5 | internal class QUERY_STORE_WITHOUT_GROUP_ONE : FDFSRequest 6 | { 7 | public static readonly QUERY_STORE_WITHOUT_GROUP_ONE Instance = new QUERY_STORE_WITHOUT_GROUP_ONE(); 8 | 9 | private QUERY_STORE_WITHOUT_GROUP_ONE() 10 | { 11 | } 12 | 13 | public override FDFSRequest GetRequest(params object[] paramList) 14 | { 15 | QUERY_STORE_WITHOUT_GROUP_ONE queryStoreWithGroupOne = new QUERY_STORE_WITHOUT_GROUP_ONE(); 16 | queryStoreWithGroupOne.BodyBuffer = new byte[0]; 17 | queryStoreWithGroupOne.Header = new FDFSHeader(0, FDFSConstants.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE, 0); 18 | return queryStoreWithGroupOne; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/QUERY_STORE_WITH_GROUP_ONE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FastDFS.Client 4 | { 5 | internal class QUERY_STORE_WITH_GROUP_ONE : FDFSRequest 6 | { 7 | public static readonly QUERY_STORE_WITH_GROUP_ONE Instance = new QUERY_STORE_WITH_GROUP_ONE(); 8 | 9 | private QUERY_STORE_WITH_GROUP_ONE() 10 | { 11 | } 12 | 13 | public override FDFSRequest GetRequest(params object[] paramList) 14 | { 15 | if (paramList.Length == 0) 16 | { 17 | throw new FDFSException("GroupName is null"); 18 | } 19 | QUERY_STORE_WITH_GROUP_ONE queryStoreWithGroupOne = new QUERY_STORE_WITH_GROUP_ONE(); 20 | var groupName = (string) paramList[0]; 21 | var groupNameByteCount = Util.StringByteCount(groupName); 22 | if (groupNameByteCount > 16) 23 | { 24 | throw new FDFSException("GroupName is too long"); 25 | } 26 | 27 | const int length = 16; 28 | queryStoreWithGroupOne.SetBodyBuffer(length); 29 | Util.StringToByte(groupName, queryStoreWithGroupOne.BodyBuffer, 0, groupNameByteCount); 30 | if (groupNameByteCount < 16) 31 | { 32 | for (var i = groupNameByteCount; i < 16; i++) 33 | { 34 | queryStoreWithGroupOne.BodyBuffer[i] = 0; 35 | } 36 | } 37 | queryStoreWithGroupOne.Header = new FDFSHeader(length, FDFSConstants.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE, 0); 38 | return queryStoreWithGroupOne; 39 | } 40 | 41 | public class Response: IFDFSResponse 42 | { 43 | public string GroupName { get; private set; } 44 | 45 | public string IPStr { get; private set; } 46 | 47 | public int Port { get; private set; } 48 | 49 | public byte StorePathIndex { get; private set; } 50 | 51 | public void ParseBuffer(byte[] responseByte, int length) 52 | { 53 | this.GroupName = Util.ByteToString(responseByte, 0, 16); 54 | this.IPStr = Util.ByteToString(responseByte, 16, 15); 55 | this.Port = (int)Util.BufferToLong(responseByte, 31); 56 | this.StorePathIndex = responseByte[length - 1]; 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/QUERY_UPDATE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FastDFS.Client 4 | { 5 | internal class QUERY_UPDATE : FDFSRequest 6 | { 7 | public static readonly QUERY_UPDATE Instance = new QUERY_UPDATE(); 8 | 9 | private QUERY_UPDATE() 10 | { 11 | } 12 | 13 | public override FDFSRequest GetRequest(params object[] paramList) 14 | { 15 | if (paramList.Length != 2) 16 | { 17 | throw new FDFSException("param count is wrong"); 18 | } 19 | QUERY_UPDATE queryUpDate = new QUERY_UPDATE(); 20 | string groupName = (string)paramList[0]; 21 | string fileName = (string)paramList[1]; 22 | 23 | var groupNameByteCount = Util.StringByteCount(groupName); 24 | if (groupNameByteCount > 16) 25 | { 26 | throw new FDFSException("groupName is too long"); 27 | } 28 | var fileNameByteCount = Util.StringByteCount(fileName); 29 | int length = 16 + fileNameByteCount; 30 | queryUpDate.SetBodyBuffer(length); 31 | Util.StringToByte(groupName, queryUpDate.BodyBuffer, 0, groupNameByteCount); 32 | if (groupNameByteCount < 16) 33 | { 34 | for (var i = groupNameByteCount; i < 16; i++) 35 | { 36 | queryUpDate.BodyBuffer[i] = 0; 37 | } 38 | } 39 | 40 | Util.StringToByte(fileName, queryUpDate.BodyBuffer, 16, fileNameByteCount); 41 | queryUpDate.Header = new FDFSHeader(length, FDFSConstants.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE, 0); 42 | return queryUpDate; 43 | } 44 | 45 | public class Response: IFDFSResponse 46 | { 47 | public string GroupName { get; set; } 48 | 49 | public string IPStr { get; set; } 50 | 51 | public int Port { get; set; } 52 | 53 | public void ParseBuffer(byte[] responseByte, int length) 54 | { 55 | this.GroupName = Util.ByteToString(responseByte, 0, 16); 56 | this.IPStr = Util.ByteToString(responseByte, 16, 15); 57 | this.Port = (int)Util.BufferToLong(responseByte, 31); 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/SocketEx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastDFS.Client 8 | { 9 | public static class SocketEx 10 | { 11 | public static Task ConnectExAsync(this Socket socket, EndPoint remoteEp) 12 | { 13 | var tcs = new TaskCompletionSource(socket); 14 | socket.BeginConnect(remoteEp, iar => 15 | { 16 | var innerTcs = (TaskCompletionSource)iar.AsyncState; 17 | try 18 | { 19 | ((Socket)innerTcs.Task.AsyncState).EndConnect(iar); 20 | innerTcs.TrySetResult(true); 21 | } 22 | catch (Exception e) 23 | { 24 | innerTcs.TrySetException(e); 25 | } 26 | }, tcs); 27 | return tcs.Task; 28 | } 29 | 30 | public static async Task ReceiveExAsync(this Socket socket, byte[] buffer, int length) 31 | { 32 | int index = 0; 33 | while (index < length) 34 | { 35 | int read = await socket.ReceiveExAsync0(buffer, index, length - index); 36 | if (read == 0) 37 | { 38 | throw new SocketException((int) SocketError.ConnectionReset); 39 | } 40 | index += read; 41 | } 42 | return length; 43 | } 44 | 45 | private static Task ReceiveExAsync0(this Socket socket, byte[] buffer,int index, int size) 46 | { 47 | var tcs = new TaskCompletionSource(socket); 48 | socket.BeginReceive(buffer, index, size, SocketFlags.None, iar => 49 | { 50 | var innerTcs = (TaskCompletionSource)iar.AsyncState; 51 | try 52 | { 53 | var rec = ((Socket)innerTcs.Task.AsyncState).EndReceive(iar); 54 | innerTcs.TrySetResult(rec); 55 | } 56 | catch (Exception e) 57 | { 58 | innerTcs.TrySetException(e); 59 | } 60 | }, tcs); 61 | return tcs.Task; 62 | } 63 | 64 | public static Task SendExAsync(this Socket socket, IList> buffers) 65 | { 66 | var tcs = new TaskCompletionSource(socket); 67 | socket.BeginSend(buffers, SocketFlags.None, iar => 68 | { 69 | var innerTcs = (TaskCompletionSource)iar.AsyncState; 70 | try 71 | { 72 | var sent = ((Socket)innerTcs.Task.AsyncState).EndSend(iar); 73 | innerTcs.TrySetResult(sent); 74 | } 75 | catch (Exception e) 76 | { 77 | innerTcs.TrySetException(e); 78 | } 79 | }, tcs); 80 | return tcs.Task; 81 | } 82 | 83 | public static Task SendExAsync(this Socket socket, byte[] buffer, int index, int size) 84 | { 85 | var tcs = new TaskCompletionSource(socket); 86 | socket.BeginSend(buffer, index, size, SocketFlags.None, iar => 87 | { 88 | var innerTcs = (TaskCompletionSource) iar.AsyncState; 89 | try 90 | { 91 | var sent = ((Socket) innerTcs.Task.AsyncState).EndSend(iar); 92 | innerTcs.TrySetResult(sent); 93 | } 94 | catch (Exception e) 95 | { 96 | innerTcs.TrySetException(e); 97 | } 98 | }, tcs); 99 | return tcs.Task; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/FastDFS/Client/StorageNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace FastDFS.Client 5 | { 6 | public class StorageNode 7 | { 8 | public string GroupName; 9 | 10 | public IPEndPoint EndPoint; 11 | 12 | public byte StorePathIndex; 13 | 14 | public StorageNode() 15 | { 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/UPLOAD_APPEND_FILE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace FastDFS.Client 6 | { 7 | internal class UPLOAD_APPEND_FILE : FDFSRequest 8 | { 9 | public static readonly UPLOAD_APPEND_FILE Instance = new UPLOAD_APPEND_FILE(); 10 | 11 | private UPLOAD_APPEND_FILE() 12 | { 13 | } 14 | 15 | public override FDFSRequest GetRequest(params object[] paramList) 16 | { 17 | if (paramList.Length != 3) 18 | { 19 | throw new FDFSException("param count is wrong"); 20 | } 21 | 22 | StorageNode storageNode = (StorageNode)paramList[0]; 23 | string fileExt = (string) paramList[1] ?? string.Empty; 24 | if (fileExt.Length > 0 && fileExt[0] == '.') 25 | { 26 | fileExt = fileExt.Substring(1); 27 | } 28 | 29 | var contentStream = (Stream) paramList[2]; 30 | 31 | var contentByteLength = contentStream.Length; 32 | 33 | UPLOAD_APPEND_FILE uploadAppendFile = new UPLOAD_APPEND_FILE 34 | { 35 | ConnectionType = FDFSConnectionType.StorageConnection, 36 | EndPoint = storageNode.EndPoint 37 | }; 38 | 39 | if (fileExt.Length > 6) 40 | { 41 | throw new FDFSException("file ext is too long"); 42 | } 43 | 44 | const int bodyBufferLen = 15; 45 | uploadAppendFile.SetBodyBuffer(bodyBufferLen); 46 | 47 | int offset = 0; 48 | uploadAppendFile.BodyBuffer[offset++] = storageNode.StorePathIndex; 49 | 50 | Util.LongToBuffer(contentByteLength, uploadAppendFile.BodyBuffer, offset); 51 | offset += 8; 52 | 53 | var fileExtByteCount = Util.StringByteCount(fileExt); 54 | Util.StringToByte(fileExt, uploadAppendFile.BodyBuffer, offset, fileExtByteCount); 55 | if (fileExtByteCount < 6) 56 | { 57 | for (var i = offset + fileExtByteCount; i < offset + 6; i++) 58 | { 59 | uploadAppendFile.BodyBuffer[i] = 0; 60 | } 61 | } 62 | 63 | uploadAppendFile.BodyStream = contentStream; 64 | 65 | var length = bodyBufferLen + contentByteLength; 66 | uploadAppendFile.Header = new FDFSHeader(length, FDFSConstants.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, 0); 67 | return uploadAppendFile; 68 | } 69 | 70 | public class Response : IFDFSResponse 71 | { 72 | public string GroupName; 73 | 74 | public string FileName; 75 | 76 | public void ParseBuffer(byte[] responseByte, int length) 77 | { 78 | this.GroupName = Util.ByteToString(responseByte, 0, 16); 79 | this.FileName = Util.ByteToString(responseByte, 16, length - 16); 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/FastDFS/Client/UPLOAD_FILE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace FastDFS.Client 6 | { 7 | internal class UPLOAD_FILE : FDFSRequest 8 | { 9 | public static readonly UPLOAD_FILE Instance = new UPLOAD_FILE(); 10 | 11 | private UPLOAD_FILE() 12 | { 13 | } 14 | 15 | public override FDFSRequest GetRequest(params object[] paramList) 16 | { 17 | if (paramList.Length != 3) 18 | { 19 | throw new FDFSException("param count is wrong"); 20 | } 21 | 22 | StorageNode storageNode = (StorageNode) paramList[0]; 23 | string fileExt = (string) paramList[1] ?? string.Empty; 24 | if (fileExt.Length > 0 && fileExt[0] == '.') 25 | { 26 | fileExt = fileExt.Substring(1); 27 | } 28 | var contentStream = (Stream) paramList[2]; 29 | 30 | var contentByteLength = contentStream.Length; 31 | 32 | UPLOAD_FILE uploadFile = new UPLOAD_FILE 33 | { 34 | ConnectionType = FDFSConnectionType.StorageConnection, 35 | EndPoint = storageNode.EndPoint 36 | }; 37 | if (fileExt.Length > 6) 38 | { 39 | throw new FDFSException("file ext is too long"); 40 | } 41 | 42 | const int bodyBufferLen = 15; 43 | uploadFile.SetBodyBuffer(bodyBufferLen); 44 | 45 | int offset = 0; 46 | uploadFile.BodyBuffer[offset++] = storageNode.StorePathIndex; 47 | 48 | Util.LongToBuffer(contentByteLength, uploadFile.BodyBuffer, offset); 49 | offset += 8; 50 | 51 | var fileExtByteCount = Util.StringByteCount(fileExt); 52 | Util.StringToByte(fileExt, uploadFile.BodyBuffer, offset, fileExtByteCount); 53 | if (fileExtByteCount < 6) 54 | { 55 | for (var i = offset + fileExtByteCount; i < offset + 6; i++) 56 | { 57 | uploadFile.BodyBuffer[i] = 0; 58 | } 59 | } 60 | 61 | uploadFile.BodyStream = contentStream; 62 | 63 | long length = bodyBufferLen + contentStream.Length; 64 | uploadFile.Header = new FDFSHeader(length, FDFSConstants.STORAGE_PROTO_CMD_UPLOAD_FILE, 0); 65 | return uploadFile; 66 | } 67 | 68 | public class Response:IFDFSResponse 69 | { 70 | public string FileName { get; private set; } 71 | 72 | public string GroupName { get; private set; } 73 | 74 | public void ParseBuffer(byte[] responseByte, int length) 75 | { 76 | this.GroupName = Util.ByteToString(responseByte, 0, 16); 77 | this.FileName = Util.ByteToString(responseByte, 16, length - 16); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/FastDFS/Client/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace FastDFS.Client 6 | { 7 | internal static class Util 8 | { 9 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 10 | public static long BufferToLong(byte[] buffer, int offset) 11 | { 12 | return (long) buffer[offset] << 56 13 | | ((long) buffer[offset + 1] << 48) 14 | | ((long) buffer[offset + 2] << 40) 15 | | ((long) buffer[offset + 3] << 32) 16 | | ((long) buffer[offset + 4] << 24) 17 | | ((long) buffer[offset + 5] << 16) 18 | | ((long) buffer[offset + 6] << 8) 19 | | (long) buffer[offset + 7]; 20 | } 21 | 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | public static void LongToBuffer(long l, byte[] buffer, int offset) 24 | { 25 | buffer[offset] = (byte) ((l >> 56) & 0xffL); 26 | buffer[offset + 1] = (byte) ((l >> 48) & 0xffL); 27 | buffer[offset + 2] = (byte) ((l >> 40) & 0xffL); 28 | buffer[offset + 3] = (byte) ((l >> 32) & 0xffL); 29 | buffer[offset + 4] = (byte) ((l >> 24) & 0xffL); 30 | buffer[offset + 5] = (byte) ((l >> 16) & 0xffL); 31 | buffer[offset + 6] = (byte) ((l >> 8) & 0xffL); 32 | buffer[offset + 7] = (byte) (l & 0xffL); 33 | } 34 | 35 | public static unsafe string ByteToString(byte[] input, int index, int count) 36 | { 37 | Span span = FDFSConfig.Charset.GetChars(input, index, count); 38 | fixed (char* chars = &TrimEnd(span).GetPinnableReference()) 39 | { 40 | return new string(chars); 41 | } 42 | } 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | private static Span TrimEnd(Span span) 46 | { 47 | int index = span.Length - 1; 48 | while (index >= 0 && span[index] == '\0') 49 | --index; 50 | return span.Slice(0, index + 1); 51 | } 52 | 53 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 54 | public static int StringByteCount(string input) 55 | { 56 | return FDFSConfig.Charset.GetByteCount(input); 57 | } 58 | 59 | public static unsafe void StringToByte(string input, byte[] buffer, int offset, int? inputByteCount = null) 60 | { 61 | if (inputByteCount == null) 62 | { 63 | inputByteCount = StringByteCount(input); 64 | if (inputByteCount == 0) 65 | { 66 | return; 67 | } 68 | } 69 | 70 | var bytes = new Span(buffer, offset, inputByteCount.Value); 71 | var chars = input.AsSpan(); 72 | fixed (char* chars1 = &MemoryMarshal.GetReference(chars)) 73 | { 74 | fixed (byte* bytes1 = &MemoryMarshal.GetReference(bytes)) 75 | { 76 | FDFSConfig.Charset.GetBytes(chars1, chars.Length, bytes1, bytes.Length); 77 | } 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/FastDFS/FastDFS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0;net451;netcoreapp2.1 4 | 1.2.7 5 | true 6 | true 7 | false 8 | FastDFSNetCore 9 | 10 | fastdfs async client 11 | 1.2.4: fix download file return bytes bug , suggest use IDownloadCallback 12 | 1.2.3: support big file upload and download 13 | 1.2.2: support QUERY_STORE_WITHOUT_GROUP_ONE 14 | 1.2.1: compatible with DnsEndPoint and fileExt first char is a dot 15 | 1.2.0: support download file 16 | 1.1.9: support appenderfile 17 | 1.1.7: 1. string zero memory copy 2. errorcode msg 18 | 1.1.6: use span arraypool reduce gc 19 | 20 | fastdfs;tcp 21 | caozhiyuan 22 | https://github.com/caozhiyuan/FastDFSNetCore 23 | https://github.com/caozhiyuan/FastDFSNetCore/blob/master/LICENSE 24 | true 25 | true 26 | snupkg 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/FastDFS/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下 6 | // 特性集控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("FastDFS")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("suiyi")] 12 | [assembly: AssemblyProduct("FastDFSNetCore")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("2f6d19c5-763b-4b9c-84e6-e3837b6ef905")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.1.0.0")] 36 | [assembly: AssemblyFileVersion("1.1.0.0")] 37 | --------------------------------------------------------------------------------