├── .gitignore
├── .nuget
├── NuGet.Config
├── NuGet.exe
└── NuGet.targets
├── AzureBlobFileSystem.Test
├── AzureBlobFileSystem.Test.csproj
├── AzureBlobStorageProviderTest.cs
├── FileSystemStorageProviderTest.cs
├── Properties
│ └── AssemblyInfo.cs
├── StorageUniversalTest.cs
└── packages.config
├── AzureBlobFileSystem.sln
├── AzureBlobFileSystem
├── AzureBlobFileSystem.csproj
├── AzureBlobStorageProvider.cs
├── CloudBlobContainerExtensions.cs
├── FileSystemStorageProvider.cs
├── IStorageFile.cs
├── IStorageFolder.cs
├── IStorageProvider.cs
├── PathValidation.cs
├── Properties
│ └── AssemblyInfo.cs
├── SasPermissionFlags.cs
└── packages.config
├── BlobFileSystem.Azure.nuspec
├── LICENSE
├── README.md
├── Snk
└── snk.snk
└── nugetpack.cmd
/.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 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | bld/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # MSTest test Results
20 | [Tt]est[Rr]esult*/
21 | [Bb]uild[Ll]og.*
22 |
23 | #NUNIT
24 | *.VisualState.xml
25 | TestResult.xml
26 |
27 | # Build Results of an ATL Project
28 | [Dd]ebugPS/
29 | [Rr]eleasePS/
30 | dlldata.c
31 |
32 | *_i.c
33 | *_p.c
34 | *_i.h
35 | *.ilk
36 | *.meta
37 | *.obj
38 | *.pch
39 | *.pdb
40 | *.pgc
41 | *.pgd
42 | *.rsp
43 | *.sbr
44 | *.tlb
45 | *.tli
46 | *.tlh
47 | *.tmp
48 | *.tmp_proj
49 | *.log
50 | *.vspscc
51 | *.vssscc
52 | .builds
53 | *.pidb
54 | *.svclog
55 | *.scc
56 |
57 | # Chutzpah Test files
58 | _Chutzpah*
59 |
60 | # Visual C++ cache files
61 | ipch/
62 | *.aps
63 | *.ncb
64 | *.opensdf
65 | *.sdf
66 | *.cachefile
67 |
68 | # Visual Studio profiler
69 | *.psess
70 | *.vsp
71 | *.vspx
72 |
73 | # TFS 2012 Local Workspace
74 | $tf/
75 |
76 | # Guidance Automation Toolkit
77 | *.gpState
78 |
79 | # ReSharper is a .NET coding add-in
80 | _ReSharper*/
81 | *.[Rr]e[Ss]harper
82 | *.DotSettings.user
83 |
84 | # JustCode is a .NET coding addin-in
85 | .JustCode
86 |
87 | # TeamCity is a build add-in
88 | _TeamCity*
89 |
90 | # DotCover is a Code Coverage Tool
91 | *.dotCover
92 |
93 | # NCrunch
94 | *.ncrunch*
95 | _NCrunch_*
96 | .*crunch*.local.xml
97 |
98 | # MightyMoose
99 | *.mm.*
100 | AutoTest.Net/
101 |
102 | # Web workbench (sass)
103 | .sass-cache/
104 |
105 | # Installshield output folder
106 | [Ee]xpress/
107 |
108 | # DocProject is a documentation generator add-in
109 | DocProject/buildhelp/
110 | DocProject/Help/*.HxT
111 | DocProject/Help/*.HxC
112 | DocProject/Help/*.hhc
113 | DocProject/Help/*.hhk
114 | DocProject/Help/*.hhp
115 | DocProject/Help/Html2
116 | DocProject/Help/html
117 |
118 | # Click-Once directory
119 | publish/
120 |
121 | # Publish Web Output
122 | *.[Pp]ublish.xml
123 | *.azurePubxml
124 |
125 | # NuGet Packages Directory
126 | packages/
127 | ## TODO: If the tool you use requires repositories.config uncomment the next line
128 | #!packages/repositories.config
129 |
130 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
131 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
132 | !packages/build/
133 |
134 | # Windows Azure Build Output
135 | csx/
136 | *.build.csdef
137 |
138 | # Windows Store app package directory
139 | AppPackages/
140 |
141 | # Others
142 | sql/
143 | *.Cache
144 | ClientBin/
145 | [Ss]tyle[Cc]op.*
146 | ~$*
147 | *~
148 | *.dbmdl
149 | *.dbproj.schemaview
150 | *.pfx
151 | *.publishsettings
152 | node_modules/
153 |
154 | # RIA/Silverlight projects
155 | Generated_Code/
156 |
157 | # Backup & report files from converting an old project file to a newer
158 | # Visual Studio version. Backup files are not needed, because we have git ;-)
159 | _UpgradeReport_Files/
160 | Backup*/
161 | UpgradeLog*.XML
162 | UpgradeLog*.htm
163 |
164 | # SQL Server files
165 | *.mdf
166 | *.ldf
167 |
168 | # Business Intelligence projects
169 | *.rdl.data
170 | *.bim.layout
171 | *.bim_*.settings
172 |
173 | # Microsoft Fakes
174 | FakesAssemblies/
175 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pofider/AzureBlobFileSystem/873e285b3891519ecbf4cfe527f9eaa72fe08174/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 |
32 |
33 |
34 |
35 | $(SolutionDir).nuget
36 |
37 |
38 |
39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
41 |
42 |
43 |
44 | $(MSBuildProjectDirectory)\packages.config
45 | $(PackagesProjectConfig)
46 |
47 |
48 |
49 |
50 | $(NuGetToolsPath)\NuGet.exe
51 | @(PackageSource)
52 |
53 | "$(NuGetExePath)"
54 | mono --runtime=v4.0.30319 $(NuGetExePath)
55 |
56 | $(TargetDir.Trim('\\'))
57 |
58 | -RequireConsent
59 | -NonInteractive
60 |
61 | "$(SolutionDir) "
62 | "$(SolutionDir)"
63 |
64 |
65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
67 |
68 |
69 |
70 | RestorePackages;
71 | $(BuildDependsOn);
72 |
73 |
74 |
75 |
76 | $(BuildDependsOn);
77 | BuildPackage;
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/AzureBlobFileSystem.Test/AzureBlobFileSystem.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {84F6D56B-37F2-4083-A150-EC4ACD9BBA98}
7 | Library
8 | Properties
9 | AzureBlobFileSystem.Test
10 | AzureBlobFileSystem.Test
11 | v4.5
12 | 512
13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 10.0
15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
17 | False
18 | UnitTest
19 | ..\
20 | true
21 |
22 |
23 | true
24 | full
25 | false
26 | bin\Debug\
27 | DEBUG;TRACE
28 | prompt
29 | 4
30 |
31 |
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE
36 | prompt
37 | 4
38 |
39 |
40 | true
41 |
42 |
43 | ../Snk/snk.snk
44 |
45 |
46 |
47 | ..\packages\Microsoft.Data.Edm.5.6.0\lib\net40\Microsoft.Data.Edm.dll
48 |
49 |
50 | ..\packages\Microsoft.Data.OData.5.6.0\lib\net40\Microsoft.Data.OData.dll
51 |
52 |
53 | ..\packages\Microsoft.Data.Services.Client.5.6.0\lib\net40\Microsoft.Data.Services.Client.dll
54 |
55 |
56 | ..\packages\Microsoft.WindowsAzure.ConfigurationManager.1.8.0.0\lib\net35-full\Microsoft.WindowsAzure.Configuration.dll
57 |
58 |
59 | False
60 | ..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll
61 |
62 |
63 | ..\packages\Newtonsoft.Json.5.0.6\lib\net45\Newtonsoft.Json.dll
64 |
65 |
66 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll
67 |
68 |
69 |
70 |
71 | ..\packages\System.Spatial.5.6.0\lib\net40\System.Spatial.dll
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | {FA608600-4A63-497E-BB7E-57CE59EF7E61}
100 | AzureBlobFileSystem
101 |
102 |
103 |
104 |
105 |
106 |
107 | False
108 |
109 |
110 | False
111 |
112 |
113 | False
114 |
115 |
116 | False
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
127 |
128 |
129 |
130 |
137 |
--------------------------------------------------------------------------------
/AzureBlobFileSystem.Test/AzureBlobStorageProviderTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using Microsoft.WindowsAzure.Storage;
5 | using NUnit.Framework;
6 |
7 | namespace AzureBlobFileSystem.Test
8 | {
9 | [TestFixture]
10 | public class AzureBlobStorageProviderTest : StorageUniversalTest
11 | {
12 | protected override IStorageProvider CreateStorage()
13 | {
14 | CloudStorageAccount storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
15 | return new AzureBlobStorageProvider(storageAccount);
16 | }
17 |
18 | [Test]
19 | [ExpectedException(typeof(ArgumentException))]
20 | public void renameFolder_for_container_should_throw_argument_exception()
21 | {
22 | SUT.RenameFolder(TEST_FOLDER, TEST_FOLDER + "2");
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem.Test/FileSystemStorageProviderTest.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using NUnit.Framework;
4 |
5 | namespace AzureBlobFileSystem.Test
6 | {
7 | [TestFixture]
8 | public class FileSystemStorageProviderTest : StorageUniversalTest
9 | {
10 | protected override IStorageProvider CreateStorage()
11 | {
12 | return new FileSystemStorageProvider();
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem.Test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("AzureBlobFileSystem.Test")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("AzureBlobFileSystem.Test")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("6954cf0b-aa79-4b9f-a946-1a1ed54f24e4")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/AzureBlobFileSystem.Test/StorageUniversalTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using NUnit.Framework;
6 |
7 | namespace AzureBlobFileSystem.Test
8 | {
9 | [TestFixture]
10 | public abstract class StorageUniversalTest
11 | {
12 | protected IStorageProvider SUT;
13 |
14 | protected abstract IStorageProvider CreateStorage();
15 |
16 | protected string TEST_FOLDER;
17 |
18 | [SetUp]
19 | public void StorageUniversalTest_SetUp()
20 | {
21 | SUT = CreateStorage();
22 |
23 | TEST_FOLDER = Guid.NewGuid().ToString();
24 | SUT.TryCreateFolder(TEST_FOLDER);
25 | SUT.ListFiles(TEST_FOLDER).ToList().ForEach(f => SUT.DeleteFile(f.GetPath()));
26 | }
27 |
28 | [TearDown]
29 | public void StorageUniversalTest_TearDown()
30 | {
31 | SUT.ListFiles(TEST_FOLDER).ToList().ForEach(f => SUT.DeleteFile(f.GetPath()));
32 | SUT.DeleteFolder(TEST_FOLDER);
33 | }
34 |
35 | [Test]
36 | public void create_write_read_file_should_equal()
37 | {
38 | using (var stream = SUT.CreateFile(SUT.Combine(TEST_FOLDER, "test.txt")).OpenWrite())
39 | {
40 | stream.Write(new byte[] { 1 }, 0, 1);
41 | }
42 |
43 | using (var stream = SUT.GetFile(SUT.Combine(TEST_FOLDER, "test.txt")).OpenRead())
44 | {
45 | var buffer = new byte[1];
46 | stream.Read(buffer, 0, 1);
47 |
48 | Assert.AreEqual(1, buffer[0]);
49 | }
50 | }
51 |
52 | [Test]
53 | public void getSize_should_return_numOfBytes()
54 | {
55 | using (var stream = SUT.CreateFile(SUT.Combine(TEST_FOLDER, "test.txt")).OpenWrite())
56 | {
57 | stream.Write(new byte[] { 1 }, 0, 1);
58 | }
59 |
60 | Assert.AreEqual(1, SUT.GetFile(SUT.Combine(TEST_FOLDER, "test.txt")).GetSize());
61 | }
62 |
63 |
64 | [Test]
65 | public void update_file_content()
66 | {
67 | using (var stream = SUT.CreateFile(SUT.Combine(TEST_FOLDER, "test.txt")).OpenWrite())
68 | {
69 | stream.Write(new byte[] { 1 }, 0, 1);
70 | }
71 |
72 | using (var stream = SUT.GetFile(SUT.Combine(TEST_FOLDER, "test.txt")).OpenWrite())
73 | {
74 | stream.Write(new byte[] { 1, 2 }, 0, 2);
75 | }
76 |
77 | using (var stream = SUT.GetFile(SUT.Combine(TEST_FOLDER, "test.txt")).OpenRead())
78 | {
79 | var buffer = new byte[2];
80 | stream.Read(buffer, 0, 2);
81 |
82 | Assert.AreEqual(1, buffer[0]);
83 | Assert.AreEqual(2, buffer[1]);
84 | }
85 | }
86 |
87 | [Test]
88 | public void create_delete_file()
89 | {
90 | SUT.CreateFile(SUT.Combine(TEST_FOLDER, "test.txt"));
91 | SUT.DeleteFile(SUT.Combine(TEST_FOLDER, "test.txt"));
92 |
93 | Assert.AreEqual(0, SUT.ListFiles(TEST_FOLDER).Count());
94 | }
95 |
96 | [Test]
97 | public void fileExists_should_be_false_for_not_existing()
98 | {
99 | Assert.IsFalse(SUT.FileExists(SUT.Combine(TEST_FOLDER, "notexisting.txt")));
100 | }
101 |
102 | [Test]
103 | public void fileExists_should_be_true_for_existing()
104 | {
105 | SUT.CreateFile(SUT.Combine(TEST_FOLDER, "test.txt"));
106 | Assert.IsTrue(SUT.FileExists(SUT.Combine(TEST_FOLDER, "test.txt")));
107 | }
108 |
109 | [Test]
110 | public void renameFile()
111 | {
112 | SUT.CreateFile(SUT.Combine(TEST_FOLDER, "old.txt"));
113 |
114 | SUT.RenameFile(SUT.Combine(TEST_FOLDER, "old.txt"), SUT.Combine(TEST_FOLDER, "new.txt"));
115 |
116 | Assert.IsTrue(SUT.FileExists(SUT.Combine(TEST_FOLDER, "new.txt")));
117 | Assert.IsFalse(SUT.FileExists(SUT.Combine(TEST_FOLDER, "old.txt")));
118 | }
119 |
120 | [Test]
121 | public void createFolder_and_listFolder_shoud_contain_it()
122 | {
123 | SUT.CreateFolder(SUT.Combine(TEST_FOLDER, "folder"));
124 |
125 | Assert.AreEqual("folder", SUT.ListFolders(TEST_FOLDER).Single().GetName());
126 | }
127 |
128 | [Test]
129 | public void listFiles_should_contain_files_from_folder()
130 | {
131 | var folder = SUT.Combine(TEST_FOLDER, "folder");
132 | SUT.CreateFolder(folder);
133 |
134 | SUT.CreateFile(SUT.Combine(folder, "test.txt"));
135 |
136 | Assert.AreEqual("test.txt", SUT.ListFiles(folder).Single().GetName());
137 | }
138 |
139 | [Test]
140 | public void deleteFolder_should_remove_it_from_folder_list()
141 | {
142 | var folder = SUT.Combine(TEST_FOLDER, "folder");
143 | SUT.CreateFolder(folder);
144 | SUT.DeleteFolder(folder);
145 |
146 | Assert.AreEqual(0, SUT.ListFolders(TEST_FOLDER).Count());
147 | }
148 |
149 | [Test]
150 | public void deleteFolder_should_remove_nested_folders()
151 | {
152 | var folder = SUT.Combine(TEST_FOLDER, "folder");
153 | SUT.CreateFolder(folder);
154 |
155 | var folder2 = SUT.Combine(folder, "folder2");
156 | SUT.CreateFolder(folder2);
157 |
158 | var folder3 = SUT.Combine(folder2, "folder");
159 | SUT.CreateFolder(folder3);
160 |
161 | SUT.DeleteFolder(folder);
162 |
163 | Assert.AreEqual(0, SUT.ListFolders(TEST_FOLDER).Count());
164 | Assert.AreEqual(0, SUT.ListFolders(folder2).Count());
165 | }
166 |
167 | [Test]
168 | public void deleteFolder_should_remove_nested_files()
169 | {
170 | var folder = SUT.Combine(TEST_FOLDER, "folder");
171 | SUT.CreateFolder(folder);
172 |
173 | SUT.CreateFile(SUT.Combine(folder, "f1"));
174 |
175 | var folder2 = SUT.Combine(folder, "folder2");
176 | SUT.CreateFolder(folder2);
177 |
178 | SUT.CreateFile(SUT.Combine(folder2, "f2"));
179 |
180 | SUT.DeleteFolder(folder);
181 |
182 | Assert.AreEqual(0, SUT.ListFolders(TEST_FOLDER).Count());
183 | Assert.AreEqual(0, SUT.ListFiles(folder).Count());
184 | Assert.AreEqual(0, SUT.ListFiles(folder2).Count());
185 | }
186 |
187 | [Test]
188 | public void renameFolder_should_change_path()
189 | {
190 | var folder = SUT.Combine(TEST_FOLDER, "folder");
191 | SUT.CreateFolder(folder);
192 |
193 | SUT.RenameFolder(folder, folder + "2");
194 |
195 | Assert.AreEqual("folder2", SUT.ListFolders(TEST_FOLDER).Single().GetName());
196 | }
197 |
198 | [Test]
199 | public void renameFolder_should_mode_inner_files()
200 | {
201 | var folder = SUT.Combine(TEST_FOLDER, "folder");
202 | SUT.CreateFolder(folder);
203 |
204 | SUT.CreateFile(SUT.Combine(folder, "f1"));
205 |
206 | SUT.RenameFolder(folder, folder + "2");
207 |
208 | Assert.AreEqual("f1", SUT.ListFiles(folder + "2").Single().GetName());
209 | }
210 |
211 | [Test]
212 | public void sharedPath_should_return_when_given_offset()
213 | {
214 | var folder = SUT.Combine(TEST_FOLDER, "folder");
215 | SUT.CreateFolder(folder);
216 |
217 | SUT.CreateFile(SUT.Combine(folder, "f1"));
218 |
219 | var offset = DateTimeOffset.MaxValue;
220 |
221 | Assert.DoesNotThrow(() =>
222 | {
223 | var file = SUT.ListFiles(folder).First();
224 | var path = file.GetPath();
225 | var sharedPath = file.GetSharedAccessPath(offset);
226 | Trace.WriteLine(path);
227 | Trace.WriteLine(sharedPath);
228 | });
229 | }
230 |
231 | [Test]
232 | public void sharedPath_should_return_when_not_given_offset()
233 | {
234 | var folder = SUT.Combine(TEST_FOLDER, "folder");
235 | SUT.CreateFolder(folder);
236 |
237 | SUT.CreateFile(SUT.Combine(folder, "f1"));
238 |
239 | Assert.DoesNotThrow(() =>
240 | {
241 | var file = SUT.ListFiles(folder).First();
242 | var path = file.GetPath();
243 | var sharedPath = file.GetSharedAccessPath();
244 | Trace.WriteLine(path);
245 | Trace.WriteLine(sharedPath);
246 | });
247 | }
248 | }
249 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem.Test/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AzureBlobFileSystem.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureBlobFileSystem", "AzureBlobFileSystem\AzureBlobFileSystem.csproj", "{FA608600-4A63-497E-BB7E-57CE59EF7E61}"
5 | EndProject
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1C5F987C-F859-439D-BC11-82DBA4C641CA}"
7 | ProjectSection(SolutionItems) = preProject
8 | .nuget\NuGet.Config = .nuget\NuGet.Config
9 | .nuget\NuGet.exe = .nuget\NuGet.exe
10 | .nuget\NuGet.targets = .nuget\NuGet.targets
11 | EndProjectSection
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureBlobFileSystem.Test", "AzureBlobFileSystem.Test\AzureBlobFileSystem.Test.csproj", "{84F6D56B-37F2-4083-A150-EC4ACD9BBA98}"
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AEA43007-A07B-4E1F-9ADD-707A9D1EDBC3}"
16 | ProjectSection(SolutionItems) = preProject
17 | BlobFileSystem.Azure.nuspec = BlobFileSystem.Azure.nuspec
18 | nugetpack.cmd = nugetpack.cmd
19 | EndProjectSection
20 | EndProject
21 | Global
22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
23 | Debug|Any CPU = Debug|Any CPU
24 | Release|Any CPU = Release|Any CPU
25 | EndGlobalSection
26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
27 | {FA608600-4A63-497E-BB7E-57CE59EF7E61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {FA608600-4A63-497E-BB7E-57CE59EF7E61}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {FA608600-4A63-497E-BB7E-57CE59EF7E61}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {FA608600-4A63-497E-BB7E-57CE59EF7E61}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {84F6D56B-37F2-4083-A150-EC4ACD9BBA98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {84F6D56B-37F2-4083-A150-EC4ACD9BBA98}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {84F6D56B-37F2-4083-A150-EC4ACD9BBA98}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {84F6D56B-37F2-4083-A150-EC4ACD9BBA98}.Release|Any CPU.Build.0 = Release|Any CPU
35 | EndGlobalSection
36 | GlobalSection(SolutionProperties) = preSolution
37 | HideSolutionNode = FALSE
38 | EndGlobalSection
39 | EndGlobal
40 |
--------------------------------------------------------------------------------
/AzureBlobFileSystem/AzureBlobFileSystem.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {FA608600-4A63-497E-BB7E-57CE59EF7E61}
8 | Library
9 | Properties
10 | AzureBlobFileSystem
11 | AzureBlobFileSystem
12 | v4.5
13 | 512
14 | ..\
15 | true
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 | true
36 |
37 |
38 | ../Snk/snk.snk
39 |
40 |
41 |
42 | ..\packages\Microsoft.Data.Edm.5.6.0\lib\net40\Microsoft.Data.Edm.dll
43 |
44 |
45 | ..\packages\Microsoft.Data.OData.5.6.0\lib\net40\Microsoft.Data.OData.dll
46 |
47 |
48 | ..\packages\Microsoft.Data.Services.Client.5.6.0\lib\net40\Microsoft.Data.Services.Client.dll
49 |
50 |
51 | ..\packages\Microsoft.WindowsAzure.ConfigurationManager.1.8.0.0\lib\net35-full\Microsoft.WindowsAzure.Configuration.dll
52 |
53 |
54 | False
55 | ..\packages\WindowsAzure.Storage.3.1.0.1\lib\net40\Microsoft.WindowsAzure.Storage.dll
56 |
57 |
58 | ..\packages\Newtonsoft.Json.5.0.6\lib\net45\Newtonsoft.Json.dll
59 |
60 |
61 |
62 |
63 |
64 | ..\packages\System.Spatial.5.6.0\lib\net40\System.Spatial.dll
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
92 |
93 |
94 |
95 |
102 |
--------------------------------------------------------------------------------
/AzureBlobFileSystem/AzureBlobStorageProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Xml.Linq;
7 | using System.Xml.XPath;
8 | using Microsoft.Win32;
9 | using Microsoft.WindowsAzure.Storage;
10 | using Microsoft.WindowsAzure.Storage.Blob;
11 |
12 | namespace AzureBlobFileSystem
13 | {
14 | public class AzureBlobStorageProvider : IStorageProvider
15 | {
16 | private readonly CloudStorageAccount _storageAccount;
17 | private Object _lock = new Object();
18 | public CloudBlobClient BlobClient { get; private set; }
19 | public IList Containers { get; private set; }
20 | public Func ContainerFactory;
21 |
22 | public AzureBlobStorageProvider(CloudStorageAccount storageAccount)
23 | {
24 | _storageAccount = storageAccount;
25 | BlobClient = _storageAccount.CreateCloudBlobClient();
26 | Containers = new List();
27 | ContainerFactory = CreateContainer;
28 | }
29 |
30 | private CloudBlobContainer EnsurePathIsRelativeAndEnsureContainer(ref string path)
31 | {
32 | var containerName = path.Split('/').First();
33 |
34 | CloudBlobContainer container;
35 | lock (_lock)
36 | {
37 | container = Containers.SingleOrDefault(c => c.Name == containerName);
38 |
39 | if (container == null)
40 | {
41 | container = BlobClient.GetContainerReference(containerName);
42 |
43 | if (!container.Exists())
44 | {
45 | container = ContainerFactory(containerName);
46 | }
47 |
48 | Containers.Add(container);
49 | }
50 | }
51 |
52 | if (path.StartsWith("/") || path.StartsWith("http://") || path.StartsWith("https://"))
53 | throw new ArgumentException("Path must be relative");
54 |
55 | path = string.Join("/", path.Split('/').Skip(1));
56 |
57 | return container;
58 | }
59 |
60 | public string Combine(string path1, string path2)
61 | {
62 | if (path1 == null)
63 | {
64 | throw new ArgumentNullException("path1");
65 | }
66 |
67 | if (path2 == null)
68 | {
69 | throw new ArgumentNullException("path2");
70 | }
71 |
72 | if (String.IsNullOrEmpty(path2))
73 | {
74 | return path1;
75 | }
76 |
77 | if (String.IsNullOrEmpty(path1))
78 | {
79 | return path2;
80 | }
81 |
82 | if (path2.StartsWith("http://") || path2.StartsWith("https://"))
83 | {
84 | return path2;
85 | }
86 |
87 | var ch = path1[path1.Length - 1];
88 |
89 | if (ch != '/')
90 | {
91 | return (path1.TrimEnd('/') + '/' + path2.TrimStart('/'));
92 | }
93 |
94 | return (path1 + path2);
95 | }
96 |
97 | public IStorageFile GetFile(string path)
98 | {
99 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
100 |
101 | container.EnsureBlobExists(path);
102 | return new AzureBlobFileStorage(container.GetBlockBlobReference(path), this);
103 | }
104 |
105 | public bool FileExists(string path)
106 | {
107 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
108 |
109 | return container.BlobExists(path);
110 | }
111 |
112 | public DateTimeOffset? DefaultSharedAccessExpiration { get; set; }
113 |
114 | public IEnumerable ListFiles(string path)
115 | {
116 | path = path ?? String.Empty;
117 |
118 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
119 |
120 | string prefix = Combine(container.Name, path);
121 |
122 | if (!prefix.EndsWith("/"))
123 | prefix += "/";
124 |
125 |
126 | var result = BlobClient
127 | .ListBlobs(prefix)
128 | .OfType()
129 | .Where(b => !b.Name.EndsWith("/"))//filter out virtual folder files
130 | .Select(blobItem => new AzureBlobFileStorage(blobItem, this))
131 | .ToArray();
132 |
133 | return result;
134 | }
135 |
136 | public IEnumerable ListFolders(string path)
137 | {
138 | path = path ?? String.Empty;
139 |
140 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
141 |
142 | // return root folders
143 | if (path == String.Empty)
144 | {
145 | return container.ListBlobs()
146 | .OfType()
147 | .Select(d => new AzureBlobFolderStorage(d, this))
148 | .ToList();
149 | }
150 |
151 | return container.GetDirectoryReference(path)
152 | .ListBlobs()
153 | .OfType()
154 | .Select(d => new AzureBlobFolderStorage(d, this))
155 | .ToList();
156 | }
157 |
158 | public bool TryCreateFolder(string path)
159 | {
160 | try
161 | {
162 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
163 |
164 | if (!container.DirectoryExists(path))
165 | {
166 | CreateFolder(path);
167 | return true;
168 | }
169 |
170 | // return false to be consistent with FileSystemProvider's implementation
171 | return false;
172 | }
173 | catch
174 | {
175 | return false;
176 | }
177 | }
178 |
179 | public void CreateFolder(string path)
180 | {
181 | string fullPath = path;
182 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
183 |
184 | container.EnsureDirectoryDoesNotExist(path);
185 |
186 | CreateFile(fullPath.Trim('/') + "/");
187 | }
188 |
189 | public void DeleteFolder(string path)
190 | {
191 | var fullPath = path;
192 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
193 |
194 | if (path == "")
195 | {
196 | Containers.Remove(container);
197 | container.Delete();
198 | return;
199 | }
200 |
201 | container.EnsureDirectoryExists(path);
202 | foreach (var blob in container.GetDirectoryReference(path).ListBlobs())
203 | {
204 | if (blob is CloudBlockBlob)
205 | ((CloudBlockBlob)blob).Delete();
206 |
207 | if (blob is CloudBlobDirectory)
208 | DeleteFolder(new AzureBlobFolderStorage((CloudBlobDirectory)blob, this).GetPath());
209 | }
210 | }
211 |
212 | public void RenameFolder(string path, string newPath)
213 | {
214 | var fullNewPath = newPath;
215 |
216 | EnsurePathIsRelativeAndEnsureContainer(ref path);
217 | var container = EnsurePathIsRelativeAndEnsureContainer(ref newPath);
218 |
219 | if (path == "")
220 | throw new ArgumentException("Renaming root folders represented by azure containers is not currently supported", path);
221 |
222 | if (!path.EndsWith("/"))
223 | path += "/";
224 |
225 | if (!fullNewPath.EndsWith("/"))
226 | fullNewPath += "/";
227 |
228 | foreach (var blob in container.GetDirectoryReference(path).ListBlobs())
229 | {
230 | if (blob is CloudBlockBlob)
231 | {
232 | var azureBlob = new AzureBlobFileStorage((CloudBlockBlob)blob, this);
233 | RenameFile(azureBlob.GetPath(), Combine(fullNewPath, azureBlob.GetName()));
234 | }
235 |
236 | if (blob is CloudBlobDirectory)
237 | {
238 | var azureFolder = new AzureBlobFolderStorage((CloudBlobDirectory)blob, this);
239 | RenameFolder(azureFolder.GetPath(), Path.Combine(fullNewPath, azureFolder.GetName()));
240 | }
241 | }
242 | }
243 |
244 | public void DeleteFile(string path)
245 | {
246 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
247 |
248 | container.EnsureBlobExists(path);
249 | var blob = container.GetBlockBlobReference(path);
250 | blob.Delete();
251 | }
252 |
253 | public void RenameFile(string path, string newPath)
254 | {
255 | EnsurePathIsRelativeAndEnsureContainer(ref path);
256 | var container = EnsurePathIsRelativeAndEnsureContainer(ref newPath);
257 |
258 | container.EnsureBlobExists(path);
259 | container.EnsureBlobDoesNotExist(newPath);
260 |
261 | var blob = container.GetBlockBlobReference(path);
262 | var newBlob = container.GetBlockBlobReference(newPath);
263 | newBlob.StartCopyFromBlob(blob);
264 | blob.Delete();
265 | }
266 |
267 | public IStorageFile CreateFile(string path)
268 | {
269 | var container = EnsurePathIsRelativeAndEnsureContainer(ref path);
270 |
271 | if (container.BlobExists(path))
272 | {
273 | throw new ArgumentException("File " + path + " already exists");
274 | }
275 |
276 | var blob = container.GetBlockBlobReference(path);
277 | var contentType = GetContentType(path);
278 | if (!String.IsNullOrWhiteSpace(contentType))
279 | {
280 | blob.Properties.ContentType = contentType;
281 | }
282 |
283 | blob.UploadFromByteArray(new byte[0], 0, 0);
284 | return new AzureBlobFileStorage(blob, this);
285 | }
286 |
287 | public bool TrySaveStream(string path, Stream inputStream)
288 | {
289 | try
290 | {
291 | SaveStream(path, inputStream);
292 | }
293 | catch
294 | {
295 | return false;
296 | }
297 |
298 | return true;
299 | }
300 |
301 | public void SaveStream(string path, Stream inputStream)
302 | {
303 | // Create the file.
304 | // The CreateFile method will map the still relative path
305 | var file = CreateFile(path);
306 |
307 | using (var outputStream = file.OpenWrite())
308 | {
309 | var buffer = new byte[8192];
310 | for (; ; )
311 | {
312 | var length = inputStream.Read(buffer, 0, buffer.Length);
313 | if (length <= 0)
314 | break;
315 | outputStream.Write(buffer, 0, length);
316 | }
317 | }
318 | }
319 |
320 | ///
321 | /// Returns the mime-type of the specified file path, looking into IIS configuration and the Registry
322 | ///
323 | private string GetContentType(string path)
324 | {
325 | string extension = Path.GetExtension(path);
326 | if (String.IsNullOrWhiteSpace(extension))
327 | {
328 | return "application/unknown";
329 | }
330 |
331 | try
332 | {
333 | string applicationHost = System.Environment.ExpandEnvironmentVariables(@"%windir%\system32\inetsrv\config\applicationHost.config");
334 | //string webConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/").FilePath;
335 |
336 | // search for custom mime types in web.config and applicationhost.config
337 | foreach (var configFile in new[] { /*webConfig,*/ applicationHost })
338 | {
339 | if (File.Exists(configFile))
340 | {
341 | var xdoc = XDocument.Load(configFile);
342 | var mimeMap = xdoc.XPathSelectElements("//staticContent/mimeMap[@fileExtension='" + extension + "']").FirstOrDefault();
343 | if (mimeMap != null)
344 | {
345 | var mimeType = mimeMap.Attribute("mimeType");
346 | if (mimeType != null)
347 | {
348 | return mimeType.Value;
349 | }
350 | }
351 | }
352 | }
353 |
354 | // search into the registry
355 | RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(extension.ToLower());
356 | if (regKey != null)
357 | {
358 | var contentType = regKey.GetValue("Content Type");
359 | if (contentType != null)
360 | {
361 | return contentType.ToString();
362 | }
363 | }
364 | }
365 | catch
366 | {
367 | // if an exception occured return application/unknown
368 | return "application/unknown";
369 | }
370 |
371 | return "application/unknown";
372 | }
373 |
374 | private CloudBlobContainer CreateContainer(string name)
375 | {
376 | var container = BlobClient.GetContainerReference(name);
377 | container.CreateIfNotExists();
378 | return container;
379 | }
380 |
381 | private class AzureBlobFileStorage : IStorageFile
382 | {
383 | private CloudBlockBlob _blob;
384 | private readonly AzureBlobStorageProvider _azureFileSystem;
385 | private bool _hasFetchedAttributes = false;
386 |
387 | public AzureBlobFileStorage(CloudBlockBlob blob, AzureBlobStorageProvider azureFileSystem)
388 | {
389 | _blob = blob;
390 | _azureFileSystem = azureFileSystem;
391 | }
392 |
393 | private void EnsureAttributes()
394 | {
395 | if (_hasFetchedAttributes)
396 | return;
397 |
398 | _blob.FetchAttributes();
399 | _hasFetchedAttributes = true;
400 | }
401 |
402 | public string GetPath()
403 | {
404 | return _azureFileSystem.Combine(_blob.Container.Name, _blob.Name);
405 | }
406 |
407 | public string GetName()
408 | {
409 | return Path.GetFileName(GetPath());
410 | }
411 |
412 | public long GetSize()
413 | {
414 | EnsureAttributes();
415 | return _blob.Properties.Length;
416 | }
417 |
418 | public DateTime GetLastUpdated()
419 | {
420 | EnsureAttributes();
421 | return _blob.Properties.LastModified == null ? DateTime.MinValue : _blob.Properties.LastModified.Value.UtcDateTime;
422 | }
423 |
424 | public string GetFileType()
425 | {
426 | return Path.GetExtension(GetPath());
427 | }
428 |
429 | public string GetSharedAccessPath(DateTimeOffset? expiration = null, SasPermissionFlags permissions = SasPermissionFlags.Read)
430 | {
431 | var sasToken = _blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
432 | {
433 | SharedAccessExpiryTime = expiration ?? _azureFileSystem.DefaultSharedAccessExpiration,
434 | Permissions = GetSharedAccessBlobPermissions(permissions)
435 | });
436 |
437 | return string.Format(CultureInfo.InvariantCulture, "{0}{1}", _blob.Uri, sasToken);
438 | }
439 |
440 | public Stream OpenRead()
441 | {
442 | return _blob.OpenRead();
443 | }
444 |
445 | public Stream OpenWrite()
446 | {
447 | return _blob.OpenWrite();
448 | }
449 |
450 | public Stream CreateFile()
451 | {
452 | // as opposed to the File System implementation, if nothing is done on the stream
453 | // the file will be emptied, because Azure doesn't implement FileMode.Truncate
454 | _blob.DeleteIfExists();
455 | _blob = _blob.Container.GetBlockBlobReference(_blob.Uri.ToString());
456 | _blob.OpenWrite().Dispose(); // force file creation
457 |
458 | return OpenWrite();
459 | }
460 |
461 | private static SharedAccessBlobPermissions GetSharedAccessBlobPermissions(SasPermissionFlags flags)
462 | {
463 | return (SharedAccessBlobPermissions)flags;
464 | }
465 | }
466 |
467 | private class AzureBlobFolderStorage : IStorageFolder
468 | {
469 | private readonly CloudBlobDirectory _blob;
470 | private readonly AzureBlobStorageProvider _azureFileSystem;
471 |
472 | public AzureBlobFolderStorage(CloudBlobDirectory blob, AzureBlobStorageProvider azureFileSystem)
473 | {
474 | _blob = blob;
475 | _azureFileSystem = azureFileSystem;
476 | }
477 |
478 | public string GetName()
479 | {
480 | return _blob.Prefix.Trim('/');
481 | }
482 |
483 | public string GetPath()
484 | {
485 | return _blob.Uri.ToString().Replace(_blob.Container.Uri.ToString(), _blob.Container.Name).Trim('/');
486 | }
487 |
488 | public long GetSize()
489 | {
490 | return GetDirectorySize(_blob);
491 | }
492 |
493 | public DateTime GetLastUpdated()
494 | {
495 | return DateTime.MinValue;
496 | }
497 |
498 | public IStorageFolder GetParent()
499 | {
500 | if (_blob.Parent != null)
501 | {
502 | return new AzureBlobFolderStorage(_blob.Parent, _azureFileSystem);
503 | }
504 | throw new ArgumentException("Directory " + _blob.Uri + " does not have a parent directory");
505 | }
506 |
507 | private static long GetDirectorySize(CloudBlobDirectory directoryBlob)
508 | {
509 | long size = 0;
510 |
511 | foreach (var blobItem in directoryBlob.ListBlobs())
512 | {
513 | if (blobItem is CloudBlockBlob)
514 | size += ((CloudBlockBlob)blobItem).Properties.Length;
515 |
516 | if (blobItem is CloudBlobDirectory)
517 | size += GetDirectorySize((CloudBlobDirectory)blobItem);
518 | }
519 |
520 | return size;
521 | }
522 | }
523 | }
524 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/CloudBlobContainerExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Microsoft.WindowsAzure.Storage.Blob;
4 |
5 | namespace AzureBlobFileSystem
6 | {
7 | public static class CloudBlobContainerExtensions
8 | {
9 | public static bool BlobExists(this CloudBlobContainer container, string path)
10 | {
11 | if (String.IsNullOrEmpty(path) || path.Trim() == String.Empty)
12 | throw new ArgumentException("Path can't be empty");
13 |
14 | return container.GetBlockBlobReference(path.Replace("\\", "/")).Exists();
15 | }
16 |
17 | public static void EnsureBlobExists(this CloudBlobContainer container, string path)
18 | {
19 | if (!BlobExists(container, path))
20 | {
21 | throw new ArgumentException("File " + path + " does not exist");
22 | }
23 | }
24 |
25 | public static void EnsureBlobDoesNotExist(this CloudBlobContainer container, string path)
26 | {
27 | if (BlobExists(container, path))
28 | {
29 | throw new ArgumentException("File " + path + " already exists");
30 | }
31 | }
32 |
33 | public static bool DirectoryExists(this CloudBlobContainer container, string path)
34 | {
35 | if (String.IsNullOrEmpty(path) || path.Trim() == String.Empty)
36 | throw new ArgumentException("Path can't be empty");
37 |
38 | return container.GetDirectoryReference(path).ListBlobs().Count() > 0;
39 | }
40 |
41 | public static void EnsureDirectoryExists(this CloudBlobContainer container, string path)
42 | {
43 | if (!DirectoryExists(container, path))
44 | {
45 | throw new ArgumentException("Directory " + path + " does not exist");
46 | }
47 | }
48 |
49 | public static void EnsureDirectoryDoesNotExist(this CloudBlobContainer container, string path)
50 | {
51 | if (DirectoryExists(container, path))
52 | {
53 | throw new ArgumentException("Directory " + path + " already exists");
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/FileSystemStorageProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Web.Hosting;
6 |
7 | namespace AzureBlobFileSystem
8 | {
9 | public class FileSystemStorageProvider : IStorageProvider
10 | {
11 | private readonly string _storagePath;
12 |
13 | public FileSystemStorageProvider(string rootPath = null)
14 | {
15 | _storagePath = rootPath ?? Environment.GetEnvironmentVariable("temp");
16 | }
17 |
18 | ///
19 | /// Maps a relative path into the storage path.
20 | ///
21 | /// The relative path to be mapped.
22 | /// The relative path combined with the storage path.
23 | private string MapStorage(string path)
24 | {
25 | string mappedPath = string.IsNullOrEmpty(path) ? _storagePath : Path.Combine(_storagePath, path);
26 | return PathValidation.ValidatePath(_storagePath, mappedPath);
27 | }
28 |
29 | private static string Fix(string path)
30 | {
31 | return string.IsNullOrEmpty(path)
32 | ? ""
33 | : Path.DirectorySeparatorChar != '/'
34 | ? path.Replace('/', Path.DirectorySeparatorChar)
35 | : path;
36 | }
37 |
38 | #region Implementation of IStorageProvider
39 |
40 |
41 | ///
42 | /// Retrieves a file within the storage provider.
43 | ///
44 | /// The relative path to the file within the storage provider.
45 | /// The file.
46 | /// If the file is not found.
47 | public IStorageFile GetFile(string path)
48 | {
49 | FileInfo fileInfo = new FileInfo(MapStorage(path));
50 | if (!fileInfo.Exists)
51 | {
52 | throw new ArgumentException(string.Format("File {0} does not exist", path));
53 | }
54 |
55 | return new FileSystemStorageFile(Fix(path), fileInfo);
56 | }
57 |
58 | ///
59 | /// Lists the files within a storage provider's path.
60 | ///
61 | /// The relative path to the folder which files to list.
62 | /// The list of files in the folder.
63 | public IEnumerable ListFiles(string path)
64 | {
65 | var directoryInfo = new DirectoryInfo(MapStorage(path));
66 | if (!directoryInfo.Exists)
67 | {
68 | return Enumerable.Empty();
69 | }
70 |
71 | return directoryInfo
72 | .GetFiles()
73 | .Where(fi => !IsHidden(fi))
74 | .Select(fi => new FileSystemStorageFile(Path.Combine(Fix(path), fi.Name), fi))
75 | .ToList();
76 | }
77 |
78 | ///
79 | /// Lists the folders within a storage provider's path.
80 | ///
81 | /// The relative path to the folder which folders to list.
82 | /// The list of folders in the folder.
83 | public IEnumerable ListFolders(string path)
84 | {
85 | var directoryInfo = new DirectoryInfo(MapStorage(path));
86 | if (!directoryInfo.Exists)
87 | {
88 | try
89 | {
90 | directoryInfo.Create();
91 | }
92 | catch (Exception ex)
93 | {
94 | throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", path, ex));
95 | }
96 | }
97 |
98 | return directoryInfo
99 | .GetDirectories()
100 | .Where(di => !IsHidden(di))
101 | .Select(di => new FileSystemStorageFolder(Path.Combine(Fix(path), di.Name), di))
102 | .ToList();
103 | }
104 |
105 | ///
106 | /// Tries to create a folder in the storage provider.
107 | ///
108 | /// The relative path to the folder to be created.
109 | /// True if success; False otherwise.
110 | public bool TryCreateFolder(string path)
111 | {
112 | try
113 | {
114 | // prevent unnecessary exception
115 | DirectoryInfo directoryInfo = new DirectoryInfo(MapStorage(path));
116 | if (directoryInfo.Exists)
117 | {
118 | return false;
119 | }
120 |
121 | CreateFolder(path);
122 | }
123 | catch
124 | {
125 | return false;
126 | }
127 |
128 | return true;
129 | }
130 |
131 | ///
132 | /// Creates a folder in the storage provider.
133 | ///
134 | /// The relative path to the folder to be created.
135 | /// If the folder already exists.
136 | public void CreateFolder(string path)
137 | {
138 | DirectoryInfo directoryInfo = new DirectoryInfo(MapStorage(path));
139 | if (directoryInfo.Exists)
140 | {
141 | throw new ArgumentException(string.Format("Directory {0} already exists", path));
142 | }
143 |
144 | Directory.CreateDirectory(directoryInfo.FullName);
145 | }
146 |
147 | ///
148 | /// Deletes a folder in the storage provider.
149 | ///
150 | /// The relative path to the folder to be deleted.
151 | /// If the folder doesn't exist.
152 | public void DeleteFolder(string path)
153 | {
154 | DirectoryInfo directoryInfo = new DirectoryInfo(MapStorage(path));
155 | if (!directoryInfo.Exists)
156 | {
157 | throw new ArgumentException(string.Format("Directory {0} does not exist", path));
158 | }
159 |
160 | directoryInfo.Delete(true);
161 | }
162 |
163 | ///
164 | /// Renames a folder in the storage provider.
165 | ///
166 | /// The relative path to the folder to be renamed.
167 | /// The relative path to the new folder.
168 | public void RenameFolder(string oldPath, string newPath)
169 | {
170 | DirectoryInfo sourceDirectory = new DirectoryInfo(MapStorage(oldPath));
171 | if (!sourceDirectory.Exists)
172 | {
173 | throw new ArgumentException(string.Format("Directory {0} does not exist", oldPath));
174 | }
175 |
176 | DirectoryInfo targetDirectory = new DirectoryInfo(MapStorage(newPath));
177 | if (targetDirectory.Exists)
178 | {
179 | throw new ArgumentException(string.Format("Directory {0} already exists", newPath));
180 | }
181 |
182 | Directory.Move(sourceDirectory.FullName, targetDirectory.FullName);
183 | }
184 |
185 | ///
186 | /// Deletes a file in the storage provider.
187 | ///
188 | /// The relative path to the file to be deleted.
189 | /// If the file doesn't exist.
190 | public void DeleteFile(string path)
191 | {
192 | FileInfo fileInfo = new FileInfo(MapStorage(path));
193 | if (!fileInfo.Exists)
194 | {
195 | throw new ArgumentException(string.Format("File {0} does not exist", path));
196 | }
197 |
198 | fileInfo.Delete();
199 | }
200 |
201 | ///
202 | /// Renames a file in the storage provider.
203 | ///
204 | /// The relative path to the file to be renamed.
205 | /// The relative path to the new file.
206 | public void RenameFile(string oldPath, string newPath)
207 | {
208 | FileInfo sourceFileInfo = new FileInfo(MapStorage(oldPath));
209 | if (!sourceFileInfo.Exists)
210 | {
211 | throw new ArgumentException(string.Format("File {0} does not exist", oldPath));
212 | }
213 |
214 | FileInfo targetFileInfo = new FileInfo(MapStorage(newPath));
215 | if (targetFileInfo.Exists)
216 | {
217 | throw new ArgumentException(string.Format("File {0} already exists", newPath));
218 | }
219 |
220 | File.Move(sourceFileInfo.FullName, targetFileInfo.FullName);
221 | }
222 |
223 | ///
224 | /// Creates a file in the storage provider.
225 | ///
226 | /// The relative path to the file to be created.
227 | /// If the file already exists.
228 | /// The created file.
229 | public IStorageFile CreateFile(string path)
230 | {
231 | FileInfo fileInfo = new FileInfo(MapStorage(path));
232 | if (fileInfo.Exists)
233 | {
234 | throw new ArgumentException(string.Format("File {0} already exists", fileInfo.Name));
235 | }
236 |
237 | // ensure the directory exists
238 | var dirName = Path.GetDirectoryName(fileInfo.FullName);
239 | if (!Directory.Exists(dirName))
240 | {
241 | Directory.CreateDirectory(dirName);
242 | }
243 | File.WriteAllBytes(fileInfo.FullName, new byte[0]);
244 |
245 | return new FileSystemStorageFile(Fix(path), fileInfo);
246 | }
247 |
248 | ///
249 | /// Tries to save a stream in the storage provider.
250 | ///
251 | /// The relative path to the file to be created.
252 | /// The stream to be saved.
253 | /// True if success; False otherwise.
254 | public bool TrySaveStream(string path, Stream inputStream)
255 | {
256 | try
257 | {
258 | SaveStream(path, inputStream);
259 | }
260 | catch
261 | {
262 | return false;
263 | }
264 |
265 | return true;
266 | }
267 |
268 | ///
269 | /// Saves a stream in the storage provider.
270 | ///
271 | /// The relative path to the file to be created.
272 | /// The stream to be saved.
273 | /// If the stream can't be saved due to access permissions.
274 | public void SaveStream(string path, Stream inputStream)
275 | {
276 | // Create the file.
277 | // The CreateFile method will map the still relative path
278 | var file = CreateFile(path);
279 |
280 | var outputStream = file.OpenWrite();
281 | var buffer = new byte[8192];
282 | for (; ; )
283 | {
284 |
285 | var length = inputStream.Read(buffer, 0, buffer.Length);
286 | if (length <= 0)
287 | break;
288 | outputStream.Write(buffer, 0, length);
289 | }
290 | outputStream.Dispose();
291 | }
292 |
293 | ///
294 | /// Combines to paths.
295 | ///
296 | /// The parent path.
297 | /// The child path.
298 | /// The combined path.
299 | public string Combine(string path1, string path2)
300 | {
301 | return Path.Combine(path1, path2);
302 | }
303 |
304 | public bool FileExists(string path)
305 | {
306 | return new FileInfo(MapStorage(path)).Exists;
307 | }
308 |
309 | public DateTimeOffset? DefaultSharedAccessExpiration { get; set; }
310 |
311 | public IStorageFile CreateOrReplaceFile(string path)
312 | {
313 | if (FileExists(path))
314 | DeleteFile(path);
315 |
316 | return CreateFile(path);
317 | }
318 |
319 | private static bool IsHidden(FileSystemInfo di)
320 | {
321 | return (di.Attributes & FileAttributes.Hidden) != 0;
322 | }
323 |
324 | #endregion
325 |
326 | private class FileSystemStorageFile : IStorageFile
327 | {
328 | private readonly string _path;
329 | private readonly FileInfo _fileInfo;
330 |
331 | public FileSystemStorageFile(string path, FileInfo fileInfo)
332 | {
333 | _path = path;
334 | _fileInfo = fileInfo;
335 | }
336 |
337 | #region Implementation of IStorageFile
338 |
339 | public string GetPath()
340 | {
341 | return _path;
342 | }
343 |
344 | public string GetName()
345 | {
346 | return _fileInfo.Name;
347 | }
348 |
349 | public long GetSize()
350 | {
351 | return _fileInfo.Length;
352 | }
353 |
354 | public DateTime GetLastUpdated()
355 | {
356 | return _fileInfo.LastWriteTime;
357 | }
358 |
359 | public string GetFileType()
360 | {
361 | return _fileInfo.Extension;
362 | }
363 |
364 | public string GetSharedAccessPath(DateTimeOffset? expiration = null, SasPermissionFlags permissions = SasPermissionFlags.Read)
365 | {
366 | return _path;
367 | }
368 |
369 | public Stream OpenRead()
370 | {
371 | return new FileStream(_fileInfo.FullName, FileMode.Open, FileAccess.Read);
372 | }
373 |
374 | public Stream OpenWrite()
375 | {
376 | return new FileStream(_fileInfo.FullName, FileMode.Open, FileAccess.ReadWrite);
377 | }
378 |
379 | public Stream CreateFile()
380 | {
381 | return new FileStream(_fileInfo.FullName, FileMode.Truncate, FileAccess.ReadWrite);
382 | }
383 |
384 | #endregion
385 | }
386 |
387 | private class FileSystemStorageFolder : IStorageFolder
388 | {
389 | private readonly string _path;
390 | private readonly DirectoryInfo _directoryInfo;
391 |
392 | public FileSystemStorageFolder(string path, DirectoryInfo directoryInfo)
393 | {
394 | _path = path;
395 | _directoryInfo = directoryInfo;
396 | }
397 |
398 | #region Implementation of IStorageFolder
399 |
400 | public string GetPath()
401 | {
402 | return _path;
403 | }
404 |
405 | public string GetName()
406 | {
407 | return _directoryInfo.Name;
408 | }
409 |
410 | public DateTime GetLastUpdated()
411 | {
412 | return _directoryInfo.LastWriteTime;
413 | }
414 |
415 | public long GetSize()
416 | {
417 | return GetDirectorySize(_directoryInfo);
418 | }
419 |
420 | public IStorageFolder GetParent()
421 | {
422 | if (_directoryInfo.Parent != null)
423 | {
424 | return new FileSystemStorageFolder(Path.GetDirectoryName(_path), _directoryInfo.Parent);
425 | }
426 | throw new ArgumentException(string.Format("Directory {0} does not have a parent directory", _directoryInfo.Name));
427 | }
428 |
429 | #endregion
430 |
431 | private static long GetDirectorySize(DirectoryInfo directoryInfo)
432 | {
433 | long size = 0;
434 |
435 | FileInfo[] fileInfos = directoryInfo.GetFiles();
436 | foreach (FileInfo fileInfo in fileInfos)
437 | {
438 | if (!IsHidden(fileInfo))
439 | {
440 | size += fileInfo.Length;
441 | }
442 | }
443 | DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories();
444 | foreach (DirectoryInfo dInfo in directoryInfos)
445 | {
446 | if (!IsHidden(dInfo))
447 | {
448 | size += GetDirectorySize(dInfo);
449 | }
450 | }
451 |
452 | return size;
453 | }
454 | }
455 | }
456 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/IStorageFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace AzureBlobFileSystem
5 | {
6 | public interface IStorageFile
7 | {
8 | string GetPath();
9 | string GetName();
10 | long GetSize();
11 | DateTime GetLastUpdated();
12 | string GetFileType();
13 | string GetSharedAccessPath(DateTimeOffset? expiration = null, SasPermissionFlags permissions = SasPermissionFlags.Read);
14 |
15 | ///
16 | /// Creates a stream for reading from the file.
17 | ///
18 | Stream OpenRead();
19 |
20 | ///
21 | /// Creates a stream for writing to the file.
22 | ///
23 | Stream OpenWrite();
24 |
25 | ///
26 | /// Creates a stream for writing to the file, and truncates the existing content.
27 | ///
28 | Stream CreateFile();
29 | }
30 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/IStorageFolder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AzureBlobFileSystem
4 | {
5 | public interface IStorageFolder
6 | {
7 | string GetPath();
8 | string GetName();
9 | long GetSize();
10 | DateTime GetLastUpdated();
11 | IStorageFolder GetParent();
12 | }
13 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/IStorageProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace AzureBlobFileSystem
6 | {
7 | public interface IStorageProvider
8 | {
9 | ///
10 | /// Retrieves a file within the storage provider.
11 | ///
12 | /// The relative path to the file within the storage provider.
13 | /// The file.
14 | /// If the file is not found.
15 | IStorageFile GetFile(string path);
16 |
17 | ///
18 | /// Lists the files within a storage provider's path.
19 | ///
20 | /// The relative path to the folder which files to list.
21 | /// The list of files in the folder.
22 | IEnumerable ListFiles(string path);
23 |
24 | ///
25 | /// Lists the folders within a storage provider's path.
26 | ///
27 | /// The relative path to the folder which folders to list.
28 | /// The list of folders in the folder.
29 | IEnumerable ListFolders(string path);
30 |
31 | ///
32 | /// Tries to create a folder in the storage provider.
33 | ///
34 | /// The relative path to the folder to be created.
35 | /// True if success; False otherwise.
36 | bool TryCreateFolder(string path);
37 |
38 | ///
39 | /// Creates a folder in the storage provider.
40 | ///
41 | /// The relative path to the folder to be created.
42 | /// If the folder already exists.
43 | void CreateFolder(string path);
44 |
45 | ///
46 | /// Deletes a folder in the storage provider.
47 | ///
48 | /// The relative path to the folder to be deleted.
49 | /// If the folder doesn't exist.
50 | void DeleteFolder(string path);
51 |
52 | ///
53 | /// Renames a folder in the storage provider.
54 | ///
55 | /// The relative path to the folder to be renamed.
56 | /// The relative path to the new folder.
57 | void RenameFolder(string oldPath, string newPath);
58 |
59 | ///
60 | /// Deletes a file in the storage provider.
61 | ///
62 | /// The relative path to the file to be deleted.
63 | /// If the file doesn't exist.
64 | void DeleteFile(string path);
65 |
66 | ///
67 | /// Renames a file in the storage provider.
68 | ///
69 | /// The relative path to the file to be renamed.
70 | /// The relative path to the new file.
71 | void RenameFile(string oldPath, string newPath);
72 |
73 | ///
74 | /// Creates a file in the storage provider.
75 | ///
76 | /// The relative path to the file to be created.
77 | /// If the file already exists.
78 | /// The created file.
79 | IStorageFile CreateFile(string path);
80 |
81 | ///
82 | /// Tries to save a stream in the storage provider.
83 | ///
84 | /// The relative path to the file to be created.
85 | /// The stream to be saved.
86 | /// True if success; False otherwise.
87 | bool TrySaveStream(string path, Stream inputStream);
88 |
89 | ///
90 | /// Saves a stream in the storage provider.
91 | ///
92 | /// The relative path to the file to be created.
93 | /// The stream to be saved.
94 | /// If the stream can't be saved due to access permissions.
95 | void SaveStream(string path, Stream inputStream);
96 |
97 | ///
98 | /// Combines to paths.
99 | ///
100 | /// The parent path.
101 | /// The child path.
102 | /// The combined path.
103 | string Combine(string path1, string path2);
104 |
105 | bool FileExists(string path);
106 |
107 |
108 | ///
109 | /// Gets or sets the default shared access expiration date.
110 | ///
111 | ///
112 | /// The default shared access expiration date.
113 | ///
114 | DateTimeOffset? DefaultSharedAccessExpiration { get; set; }
115 | }
116 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/PathValidation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace AzureBlobFileSystem
5 | {
6 | ///
7 | /// Provides methods to validate paths.
8 | ///
9 | public static class PathValidation
10 | {
11 | ///
12 | /// Determines if a path lies within the base path boundaries.
13 | /// If not, an exception is thrown.
14 | ///
15 | /// The base path which boundaries are not to be transposed.
16 | /// The path to determine.
17 | /// The mapped path if valid.
18 | /// If the path is invalid.
19 | public static string ValidatePath(string basePath, string mappedPath)
20 | {
21 | bool valid = false;
22 |
23 | try
24 | {
25 | // Check that we are indeed within the storage directory boundaries
26 | valid = Path.GetFullPath(mappedPath).StartsWith(Path.GetFullPath(basePath), StringComparison.OrdinalIgnoreCase);
27 | }
28 | catch
29 | {
30 | // Make sure that if invalid for medium trust we give a proper exception
31 | valid = false;
32 | }
33 |
34 | if (!valid)
35 | {
36 | throw new ArgumentException("Invalid path");
37 | }
38 |
39 | return mappedPath;
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("AzureBlobFileSystem")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("AzureBlobFileSystem")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("2e2088ac-4423-4348-a70d-35ac1ef57b59")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.0.7.0")]
36 | [assembly: AssemblyFileVersion("0.0.7.0")]
37 |
--------------------------------------------------------------------------------
/AzureBlobFileSystem/SasPermissionFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.WindowsAzure.Storage.Blob;
3 |
4 | namespace AzureBlobFileSystem
5 | {
6 | ///
7 | /// Specifies the set of possible permissions for a shared access policy.
8 | /// This mirrors the flags available in the Azure Storage Client Library
9 | ///
10 | [Flags]
11 | public enum SasPermissionFlags
12 | {
13 | None = 0,
14 | Read = 1,
15 | Write = 2,
16 | Delete = 4,
17 | List = 8,
18 | }
19 | }
--------------------------------------------------------------------------------
/AzureBlobFileSystem/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/BlobFileSystem.Azure.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | BlobFileSystem.Azure
5 | File System In Azure Blob Storage
6 | Jan Blaha
7 | 0.0.0.0
8 | Jan Blaha
9 | This package provides easy file system abstraction and two implementations storing through azure blob storage or local disk. These implementations are interchangeable and you can easily switch between them in different environments.
10 | https://github.com/pofider/AzureBlobFileSystem
11 | http://www.opensource.org/licenses/mit-license.php
12 | Copyright 2014 Jan Blaha
13 | en-us
14 | azure blob storage filesystem directory abstraction
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Jan Blaha
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # File System In Azure Blob Storage
2 |
3 | Azure Blob Storage client provides a way how to structure blobs in storage using folders and it's hierarchy. This is handy but sometimes you may find it too complicated or you need an abstraction over it so you can use normal file system on your local machine or specific installation instead.
4 |
5 | This package provides easy file system abstraction and two implementations storing through azure blob storage or local disk. These implementations are interchangeable and you can easily switch between them in different environments.
6 |
7 | *Source codes of this library were partially taken from Orchard CMS project. The credit goes to Orchard team.*
8 |
9 | ## Installation
10 |
11 | Clone this repository or use nuget package...
12 | ```
13 | PM> Install-Package BlobFileSystem.Azure
14 | ```
15 |
16 | ## How it works
17 | Azure Blob Storage does not have support for directories directly. However it can partialy work with directories on the convention based level. This is using BlobFileSystem.Azure. It is looking at blob name with slashes (`folder/folder/file.png`) as it would be a file in nested directory. Renaming folder means for it then rename all blobs with specific name.
18 |
19 | **BlobFileSystem.Azure stores files in cloud container based on the first root folder**. This means that file `folder1/file1.txt` will be stored in the cloud container with name `folder1` and file `folder2/folder3/file2.png` will be stored in the container `folder2` under path `fodler3/file2.png`.
20 |
21 | ## Usage
22 |
23 | ### Instantiate storage
24 | ```c#
25 | //storage using azure blob client
26 | IStorageProvider azureStorage = new AzureBlobStorageProvider(CloudStorageAccount.DevelopmentStorageAccount);
27 |
28 | //storage using fileSystem
29 | IStorageProvider fileSystemStorage = new FileSystemStorageProvider();
30 | ```
31 |
32 | ### Operations
33 | ```c#
34 | //create folder
35 | storage.CreateFolder("folder");
36 |
37 | //delete folder
38 | storage.DeleteFolder("folder");
39 |
40 | //create and write file
41 | using (var writer = new StreamWriter(storage.CreateFile(storage.Combine("folder", "file.txt")).OpenWrite()))
42 | {
43 | }
44 |
45 | //read file
46 | using (var reader = new StreamReader(storage.GetFile(storage.Combine("folder", "file.txt")).OpenRead()))
47 | {
48 | }
49 |
50 | //delete file
51 | if (storage.FileExists(storage.Combine("folder", "file.txt")))
52 | storage.DeleteFile(storage.Combine("folder", "file.txt"));
53 |
54 | //list files
55 | storage.ListFiles("folder");
56 |
57 | //list folders
58 | storage.ListFolders("");
59 |
60 | //rename file
61 | storage.RenameFile(storage.Combine("folder", "file.txt"), storage.Combine("folder", "file2.txt"));
62 |
63 | //rename folder
64 | storage.CreateFolder(storage.Combine("folder", "child"));
65 | storage.RenameFile(storage.Combine("folder", "child"), storage.Combine("folder", "child2"));
66 | ```
67 |
68 | ## Limitations
69 | Storage support only limited subset of the original azure storage blob client. It also does not support page blobs currently.
70 |
71 | ## Contributions
72 | Contributions are welcome and I do accept pull requests. Just make shure the tests are running.
73 |
74 | To run all the tests, you need to have azure storage emulator running. To start it you can use following command:
75 | ```
76 | "C:\Program Files\Microsoft SDKs\Windows Azure\Emulator\csrun.exe" /devstore:start
77 | ```
78 |
79 | ## License
80 | The MIT License (MIT)
81 |
82 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
83 |
84 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
85 |
--------------------------------------------------------------------------------
/Snk/snk.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pofider/AzureBlobFileSystem/873e285b3891519ecbf4cfe527f9eaa72fe08174/Snk/snk.snk
--------------------------------------------------------------------------------
/nugetpack.cmd:
--------------------------------------------------------------------------------
1 | call .\.nuget\nuget pack -sym BlobFileSystem.Azure.nuspec -Symbols -Version %1 -OutputDirectory Packages
--------------------------------------------------------------------------------