├── .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 | [](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/) | [](https://www.nuget.org/packages/FastDFSNetCore/) | [](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 |
--------------------------------------------------------------------------------