├── .github
└── stale.yml
├── .gitignore
├── LICENSE
├── README.md
├── art
└── FirebaseLogo.128x128.png
├── samples
└── SimpleConsole
│ ├── Firebase.Storage.SimpleConsole.sln
│ └── SimpleConsole
│ ├── App.config
│ ├── Program.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── SimpleConsole.csproj
│ └── packages.config
├── src
├── Firebase.Storage.sln
└── Firebase.Storage
│ ├── Firebase.Storage.csproj
│ ├── FirebaseMetaData.cs
│ ├── FirebaseStorage.cs
│ ├── FirebaseStorageException.cs
│ ├── FirebaseStorageOptions.cs
│ ├── FirebaseStorageProgress.cs
│ ├── FirebaseStorageReference.cs
│ ├── FirebaseStorageTask.cs
│ └── HttpClientFactory.cs
└── test
├── Firebase.Storage.IntegrationTests
├── AuthenticationExtensions.cs
├── Firebase.Storage.IntegrationTests.csproj
├── Models
│ ├── FirebaseAuthenticationModel.cs
│ └── Secrets.cs
├── UploadWithMimeTypeTest.cs
├── secrets.json.example
└── test.jpg
└── Firebase.Storage.Tests.sln
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 180
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | - enhancement
10 | - up-for-grabs
11 | # Label to use when marking an issue as stale
12 | staleLabel: stale
13 | # Comment to post when marking an issue as stale. Set to `false` to disable
14 | markComment: >
15 | This issue has been automatically marked as stale because it has not had
16 | recent activity. It will be closed if no further activity occurs. Thank you
17 | for your contributions.
18 | # Comment to post when closing a stale issue. Set to `false` to disable
19 | closeComment: Closing the issue due to inactivity. Feel free to re-open
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # avoid test secrets file
5 | test/*/secrets.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 |
31 | # MSTest test Results
32 | [Tt]est[Rr]esult*/
33 | [Bb]uild[Ll]og.*
34 |
35 | # NUNIT
36 | *.VisualState.xml
37 | TestResult.xml
38 |
39 | # Build Results of an ATL Project
40 | [Dd]ebugPS/
41 | [Rr]eleasePS/
42 | dlldata.c
43 |
44 | # DNX
45 | project.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 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 |
113 | # MightyMoose
114 | *.mm.*
115 | AutoTest.Net/
116 |
117 | # Web workbench (sass)
118 | .sass-cache/
119 |
120 | # Installshield output folder
121 | [Ee]xpress/
122 |
123 | # DocProject is a documentation generator add-in
124 | DocProject/buildhelp/
125 | DocProject/Help/*.HxT
126 | DocProject/Help/*.HxC
127 | DocProject/Help/*.hhc
128 | DocProject/Help/*.hhk
129 | DocProject/Help/*.hhp
130 | DocProject/Help/Html2
131 | DocProject/Help/html
132 |
133 | # Click-Once directory
134 | publish/
135 |
136 | # Publish Web Output
137 | *.[Pp]ublish.xml
138 | *.azurePubxml
139 | ## TODO: Comment the next line if you want to checkin your
140 | ## web deploy settings but do note that will include unencrypted
141 | ## passwords
142 | #*.pubxml
143 |
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Windows Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Windows Store app package directory
160 | AppPackages/
161 |
162 | # Visual Studio cache files
163 | # files ending in .cache can be ignored
164 | *.[Cc]ache
165 | # but keep track of directories ending in .cache
166 | !*.[Cc]ache/
167 |
168 | # Others
169 | ClientBin/
170 | [Ss]tyle[Cc]op.*
171 | ~$*
172 | *~
173 | *.dbmdl
174 | *.dbproj.schemaview
175 | *.pfx
176 | *.publishsettings
177 | node_modules/
178 | orleans.codegen.cs
179 |
180 | # RIA/Silverlight projects
181 | Generated_Code/
182 |
183 | # Backup & report files from converting an old project file
184 | # to a newer Visual Studio version. Backup files are not needed,
185 | # because we have git ;-)
186 | _UpgradeReport_Files/
187 | Backup*/
188 | UpgradeLog*.XML
189 | UpgradeLog*.htm
190 |
191 | # SQL Server files
192 | *.mdf
193 | *.ldf
194 |
195 | # Business Intelligence projects
196 | *.rdl.data
197 | *.bim.layout
198 | *.bim_*.settings
199 |
200 | # Microsoft Fakes
201 | FakesAssemblies/
202 |
203 | # Node.js Tools for Visual Studio
204 | .ntvs_analysis.dat
205 |
206 | # Visual Studio 6 build log
207 | *.plg
208 |
209 | # Visual Studio 6 workspace options file
210 | *.opt
211 |
212 | # LightSwitch generated files
213 | GeneratedArtifacts/
214 | _Pvt_Extensions/
215 | ModelManifest.xml
216 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Step Up Labs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FirebaseStorage.net
2 | [](https://ci.appveyor.com/project/bezysoftware/firebase-storage-dotnet)
3 |
4 | Easily upload files and other content to Firebase Storage. More info in a [blog post](https://medium.com/step-up-labs/firebase-storage-c-library-d1656cc8b3c3)
5 |
6 | For Authenticating with Firebase checkout the [Firebase Authentication library](https://github.com/step-up-labs/firebase-authentication-dotnet) and related [blog post](https://medium.com/step-up-labs/firebase-authentication-c-library-8e5e1c30acc2)
7 |
8 | ## Installation
9 | ```csharp
10 | // Install release version
11 | Install-Package FirebaseStorage.net -pre
12 | ```
13 |
14 | ## Supported frameworks
15 | .NET Standard 1.1 - see https://github.com/dotnet/standard/blob/master/docs/versions.md for compatibility matrix
16 |
17 | ## Usage
18 |
19 | ```csharp
20 | // Get any Stream - it can be FileStream, MemoryStream or any other type of Stream
21 | var stream = File.Open(@"C:\Users\you\file.png", FileMode.Open);
22 |
23 | //authentication
24 | var auth = new FirebaseAuthProvider(new FirebaseConfig("api_key"));
25 | var a = await auth.SignInWithEmailAndPasswordAsync("email", "password");
26 |
27 | // Constructr FirebaseStorage, path to where you want to upload the file and Put it there
28 | var task = new FirebaseStorage(
29 | "your-bucket.appspot.com"
30 | new FirebaseStorageOptions
31 | {
32 | AuthTokenAsyncFactory = () => Task.FromResult(a.FirebaseToken),
33 | ThrowOnCancel = true,
34 | })
35 | .Child("data")
36 | .Child("random")
37 | .Child("file.png")
38 | .PutAsync(stream);
39 |
40 | // Track progress of the upload
41 | task.Progress.ProgressChanged += (s, e) => Console.WriteLine($"Progress: {e.Percentage} %");
42 |
43 | // await the task to wait until upload completes and get the download url
44 | var downloadUrl = await task;
45 | ```
46 |
--------------------------------------------------------------------------------
/art/FirebaseLogo.128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/step-up-labs/firebase-storage-dotnet/69dac6f45673d82db139fb6b0bd7b1256db164df/art/FirebaseLogo.128x128.png
--------------------------------------------------------------------------------
/samples/SimpleConsole/Firebase.Storage.SimpleConsole.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio 14
3 | VisualStudioVersion = 14.0.25420.1
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleConsole", "SimpleConsole\SimpleConsole.csproj", "{EB69C2A3-7824-49A5-9FFC-1A28A9B7AE55}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Firebase.Storage", "..\..\src\Firebase.Storage\Firebase.Storage.csproj", "{FFA7D737-9AAA-41AF-8264-0F109C906A64}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {EB69C2A3-7824-49A5-9FFC-1A28A9B7AE55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {EB69C2A3-7824-49A5-9FFC-1A28A9B7AE55}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {EB69C2A3-7824-49A5-9FFC-1A28A9B7AE55}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {EB69C2A3-7824-49A5-9FFC-1A28A9B7AE55}.Release|Any CPU.Build.0 = Release|Any CPU
19 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | EndGlobal
28 |
--------------------------------------------------------------------------------
/samples/SimpleConsole/SimpleConsole/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/samples/SimpleConsole/SimpleConsole/Program.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage.SimpleConsole
2 | {
3 | using Firebase.Auth;
4 |
5 | using System;
6 | using System.IO;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | internal class Program
12 | {
13 | private static string ApiKey = "YOUR_API_KEY";
14 | private static string Bucket = "your-bucket.appspot.com";
15 | private static string AuthEmail = "your@email.com";
16 | private static string AuthPassword = "yourpasword";
17 |
18 | private static void Main(string[] args)
19 | {
20 | Run().Wait();
21 | }
22 |
23 | private static async Task Run()
24 | {
25 | // FirebaseStorage.Put method accepts any type of stream.
26 | var stream = new MemoryStream(Encoding.ASCII.GetBytes("Hello world!"));
27 | //var stream = File.Open(@"C:\someFile.png", FileMode.Open);
28 |
29 | // of course you can login using other method, not just email+password
30 | var auth = new FirebaseAuthProvider(new FirebaseConfig(ApiKey));
31 | var a = await auth.SignInWithEmailAndPasswordAsync(AuthEmail, AuthPassword);
32 |
33 | // you can use CancellationTokenSource to cancel the upload midway
34 | var cancellation = new CancellationTokenSource();
35 |
36 | var task = new FirebaseStorage(
37 | Bucket,
38 | new FirebaseStorageOptions
39 | {
40 | AuthTokenAsyncFactory = () => Task.FromResult(a.FirebaseToken),
41 | ThrowOnCancel = true // when you cancel the upload, exception is thrown. By default no exception is thrown
42 | })
43 | .Child("receipts")
44 | .Child("test")
45 | .Child("someFile.png")
46 | .PutAsync(stream, cancellation.Token);
47 |
48 | task.Progress.ProgressChanged += (s, e) => Console.WriteLine($"Progress: {e.Percentage} %");
49 |
50 | // cancel the upload
51 | // cancellation.Cancel();
52 |
53 | try
54 | {
55 | // error during upload will be thrown when you await the task
56 | Console.WriteLine("Download link:\n" + await task);
57 | }
58 | catch (Exception ex)
59 | {
60 | Console.WriteLine("Exception was thrown: {0}", ex);
61 | }
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/samples/SimpleConsole/SimpleConsole/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("Firebase.Storage.SimpleConsole")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Firebase.Storage.SimpleConsole")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
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("eb69c2a3-7824-49a5-9ffc-1a28a9b7ae55")]
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 |
--------------------------------------------------------------------------------
/samples/SimpleConsole/SimpleConsole/SimpleConsole.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {EB69C2A3-7824-49A5-9FFC-1A28A9B7AE55}
8 | Exe
9 | Properties
10 | Firebase.Storage.SimpleConsole
11 | Firebase.Storage.SimpleConsole
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\FirebaseAuthentication.net.2.1.3\lib\portable45-net45+win8+wpa81\Firebase.Auth.dll
38 | True
39 |
40 |
41 | ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll
42 | True
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Designer
61 |
62 |
63 |
64 |
65 | {ffa7d737-9aaa-41af-8264-0f109c906a64}
66 | Firebase.Storage
67 |
68 |
69 |
70 |
77 |
--------------------------------------------------------------------------------
/samples/SimpleConsole/SimpleConsole/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/Firebase.Storage.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Firebase.Storage", "Firebase.Storage\Firebase.Storage.csproj", "{FFA7D737-9AAA-41AF-8264-0F109C906A64}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {FFA7D737-9AAA-41AF-8264-0F109C906A64}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/Firebase.Storage.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.1
5 | FirebaseStorage.net
6 | 1.0.3
7 | Step Up Labs, Inc.
8 | Easily upload files and other content to Firebase Storage.
9 | false
10 | https://github.com/step-up-labs/firebase-storage-dotnet/raw/master/art/FirebaseLogo.128x128.png
11 | https://github.com/step-up-labs/firebase-storage-dotnet
12 | https://github.com/step-up-labs/firebase-storage-dotnet/blob/master/LICENSE
13 | True
14 | True
15 | Step Up Labs, Inc. 2017
16 | Firebase Storage Google
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/FirebaseMetaData.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Firebase.Storage
7 | {
8 | ///
9 | /// Full list of meta data available here: https://firebase.google.com/docs/storage/web/file-metadata
10 | ///
11 | public class FirebaseMetaData
12 | {
13 | [JsonProperty("bucket")]
14 | public string Bucket { get; set; }
15 |
16 | [JsonProperty("generation")]
17 | public string Generation { get; set; }
18 |
19 | [JsonProperty("metageneration")]
20 | public string MetaGeneration { get; set; }
21 |
22 | [JsonProperty("fullPath")]
23 | public string FullPath { get; set; }
24 |
25 | [JsonProperty("name")]
26 | public string Name { get; set; }
27 |
28 | [JsonProperty("size")]
29 | public long Size { get; set; }
30 |
31 | [JsonProperty("contentType")]
32 | public string ContentType { get; set; }
33 |
34 | [JsonProperty("timeCreated")]
35 | public DateTime TimeCreated { get; set; }
36 |
37 | [JsonProperty("updated")]
38 | public DateTime Updated { get; set; }
39 |
40 | [JsonProperty("md5Hash")]
41 | public string Md5Hash { get; set; }
42 |
43 | [JsonProperty("contentEncoding")]
44 | public string ContentEncoding { get; set; }
45 |
46 | [JsonProperty("contentDisposition")]
47 | public string ContentDisposition { get; set; }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/FirebaseStorage.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage
2 | {
3 | ///
4 | /// Entry class into firebase storage.
5 | ///
6 | public class FirebaseStorage
7 | {
8 | ///
9 | /// Creates an instance of class.
10 | ///
11 | /// Google storage bucket. E.g. 'your-bucket.appspot.com'.
12 | /// Optional settings.
13 | public FirebaseStorage(string storageBucket, FirebaseStorageOptions options = null)
14 | {
15 | this.StorageBucket = storageBucket;
16 | this.Options = options ?? new FirebaseStorageOptions();
17 | }
18 |
19 | ///
20 | /// Gets the .
21 | ///
22 | public FirebaseStorageOptions Options
23 | {
24 | get;
25 | private set;
26 | }
27 |
28 | ///
29 | /// Gets the google storage bucket.
30 | ///
31 | public string StorageBucket
32 | {
33 | get;
34 | private set;
35 | }
36 |
37 | ///
38 | /// Constructs firebase path to the file.
39 | ///
40 | /// Name of the entity. This can be folder or a file name or full path.
41 | ///
42 | /// storage
43 | /// .Child("some")
44 | /// .Child("path")
45 | /// .Child("to/file.png");
46 | ///
47 | /// for fluid syntax.
48 | public FirebaseStorageReference Child(string childRoot)
49 | {
50 | return new FirebaseStorageReference(this, childRoot);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/FirebaseStorageException.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage
2 | {
3 | using System;
4 |
5 | public class FirebaseStorageException : Exception
6 | {
7 | public FirebaseStorageException(string url, string responseData, Exception innerException) : base(GenerateExceptionMessage(url, responseData), innerException)
8 | {
9 | this.RequestUrl = url;
10 | this.ResponseData = responseData;
11 | }
12 |
13 | ///
14 | /// Gets the original request url.
15 | ///
16 | public string RequestUrl
17 | {
18 | get;
19 | private set;
20 | }
21 |
22 | ///
23 | /// Gets the response data returned by the firebase service.
24 | ///
25 | public string ResponseData
26 | {
27 | get;
28 | private set;
29 | }
30 |
31 | private static string GenerateExceptionMessage(string requestUrl, string responseData)
32 | {
33 | return $"Exception occured while processing the request.\nUrl: {requestUrl}\nResponse: {responseData}";
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/FirebaseStorageOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage
2 | {
3 | using System;
4 | using System.Net.Http;
5 | using System.Threading.Tasks;
6 |
7 | public class FirebaseStorageOptions
8 | {
9 | ///
10 | /// Gets or sets the method for retrieving auth tokens. Default is null.
11 | ///
12 | public Func> AuthTokenAsyncFactory
13 | {
14 | get;
15 | set;
16 | }
17 |
18 | ///
19 | /// Gets or sets whether should be thrown when cancelling a running .
20 | ///
21 | public bool ThrowOnCancel
22 | {
23 | get;
24 | set;
25 | }
26 |
27 | ///
28 | /// Timeout of the . Default is 100s.
29 | ///
30 | public TimeSpan HttpClientTimeout
31 | {
32 | get;
33 | set;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/FirebaseStorageProgress.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage
2 | {
3 | public class FirebaseStorageProgress
4 | {
5 | public FirebaseStorageProgress(long position, long length)
6 | {
7 | this.Position = position;
8 | this.Length = length;
9 | this.Percentage = (int)((position / (double)length) * 100);
10 | }
11 |
12 | public long Length
13 | {
14 | get;
15 | private set;
16 | }
17 |
18 | public int Percentage
19 | {
20 | get;
21 | private set;
22 | }
23 |
24 | public long Position
25 | {
26 | get;
27 | private set;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/FirebaseStorageReference.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage
2 | {
3 | using Newtonsoft.Json;
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | public class FirebaseStorageReference
12 | {
13 | private const string FirebaseStorageEndpoint = "https://firebasestorage.googleapis.com/v0/b/";
14 |
15 | private readonly FirebaseStorage storage;
16 | private readonly List children;
17 |
18 | internal FirebaseStorageReference(FirebaseStorage storage, string childRoot)
19 | {
20 | this.children = new List();
21 |
22 | this.storage = storage;
23 | this.children.Add(childRoot);
24 | }
25 |
26 | ///
27 | /// Starts uploading given stream to target location.
28 | ///
29 | /// Stream to upload.
30 | /// Cancellation token which can be used to cancel the operation.
31 | /// Optional type of data being uploaded, will be used to set HTTP Content-Type header.
32 | /// which can be used to track the progress of the upload.
33 | public FirebaseStorageTask PutAsync(Stream stream, CancellationToken cancellationToken, string mimeType = null)
34 | {
35 | return new FirebaseStorageTask(this.storage.Options, this.GetTargetUrl(), this.GetFullDownloadUrl(), stream, cancellationToken, mimeType);
36 | }
37 |
38 | ///
39 | /// Starts uploading given stream to target location.
40 | ///
41 | /// Stream to upload.
42 | /// which can be used to track the progress of the upload.
43 | public FirebaseStorageTask PutAsync(Stream fileStream)
44 | {
45 | return this.PutAsync(fileStream, CancellationToken.None);
46 | }
47 |
48 | ///
49 | /// Gets the meta data for given file.
50 | ///
51 | ///
52 | public async Task GetMetaDataAsync()
53 | {
54 | var data = await PerformFetch();
55 |
56 | return data;
57 | }
58 |
59 | ///
60 | /// Gets the url to download given file.
61 | ///
62 | public async Task GetDownloadUrlAsync()
63 | {
64 | var data = await PerformFetch>();
65 |
66 | object downloadTokens;
67 |
68 | if (!data.TryGetValue("downloadTokens", out downloadTokens))
69 | {
70 | throw new ArgumentOutOfRangeException($"Could not extract 'downloadTokens' property from response. Response: {JsonConvert.SerializeObject(data)}");
71 | }
72 |
73 | return this.GetFullDownloadUrl() + downloadTokens;
74 | }
75 |
76 | ///
77 | /// Deletes a file at target location.
78 | ///
79 | public async Task DeleteAsync()
80 | {
81 | var url = this.GetDownloadUrl();
82 | var resultContent = "N/A";
83 |
84 | try
85 | {
86 | using (var http = await this.storage.Options.CreateHttpClientAsync().ConfigureAwait(false))
87 | {
88 | var result = await http.DeleteAsync(url).ConfigureAwait(false);
89 |
90 | resultContent = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
91 |
92 | result.EnsureSuccessStatusCode();
93 | }
94 | }
95 | catch (Exception ex)
96 | {
97 | throw new FirebaseStorageException(url, resultContent, ex);
98 | }
99 | }
100 |
101 | ///
102 | /// Constructs firebase path to the file.
103 | ///
104 | /// Name of the entity. This can be folder or a file name or full path.
105 | ///
106 | /// storage
107 | /// .Child("some")
108 | /// .Child("path")
109 | /// .Child("to/file.png");
110 | ///
111 | /// for fluid syntax.
112 | public FirebaseStorageReference Child(string name)
113 | {
114 | this.children.Add(name);
115 | return this;
116 | }
117 |
118 | private async Task PerformFetch()
119 | {
120 | var url = this.GetDownloadUrl();
121 | var resultContent = "N/A";
122 |
123 | try
124 | {
125 | using (var http = await this.storage.Options.CreateHttpClientAsync().ConfigureAwait(false))
126 | {
127 | var result = await http.GetAsync(url);
128 | resultContent = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
129 | var data = JsonConvert.DeserializeObject(resultContent);
130 |
131 | result.EnsureSuccessStatusCode();
132 |
133 | return data;
134 | }
135 | }
136 | catch (Exception ex)
137 | {
138 | throw new FirebaseStorageException(url, resultContent, ex);
139 | }
140 | }
141 |
142 | private string GetTargetUrl()
143 | {
144 | return $"{FirebaseStorageEndpoint}{this.storage.StorageBucket}/o?name={this.GetEscapedPath()}";
145 | }
146 |
147 | private string GetDownloadUrl()
148 | {
149 | return $"{FirebaseStorageEndpoint}{this.storage.StorageBucket}/o/{this.GetEscapedPath()}";
150 | }
151 |
152 | private string GetFullDownloadUrl()
153 | {
154 | return this.GetDownloadUrl() + "?alt=media&token=";
155 | }
156 |
157 | private string GetEscapedPath()
158 | {
159 | return Uri.EscapeDataString(string.Join("/", this.children));
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/FirebaseStorageTask.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage
2 | {
3 | using Newtonsoft.Json;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Net.Http;
8 | using System.Net.Http.Headers;
9 | using System.Runtime.CompilerServices;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | public class FirebaseStorageTask
14 | {
15 | private const int ProgressReportDelayMiliseconds = 500;
16 |
17 | private readonly Task uploadTask;
18 | private readonly Stream stream;
19 |
20 | public FirebaseStorageTask(FirebaseStorageOptions options, string url, string downloadUrl, Stream stream, CancellationToken cancellationToken, string mimeType = null)
21 | {
22 | this.TargetUrl = url;
23 | this.uploadTask = this.UploadFile(options, url, downloadUrl, stream, cancellationToken, mimeType);
24 | this.stream = stream;
25 | this.Progress = new Progress();
26 |
27 | Task.Factory.StartNew(this.ReportProgressLoop);
28 | }
29 |
30 | public Progress Progress
31 | {
32 | get;
33 | private set;
34 | }
35 |
36 |
37 | public string TargetUrl
38 | {
39 | get;
40 | private set;
41 | }
42 |
43 | public TaskAwaiter GetAwaiter()
44 | {
45 | return this.uploadTask.GetAwaiter();
46 | }
47 |
48 | private async Task UploadFile(FirebaseStorageOptions options, string url, string downloadUrl, Stream stream, CancellationToken cancellationToken, string mimeType = null)
49 | {
50 | var responseData = "N/A";
51 |
52 | try
53 | {
54 | using (var client = await options.CreateHttpClientAsync())
55 | {
56 | var request = new HttpRequestMessage(HttpMethod.Post, url)
57 | {
58 | Content = new StreamContent(stream)
59 | };
60 |
61 | if (!string.IsNullOrEmpty(mimeType))
62 | {
63 | request.Content.Headers.ContentType = new MediaTypeHeaderValue(mimeType);
64 | }
65 |
66 | var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
67 | responseData = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
68 |
69 | response.EnsureSuccessStatusCode();
70 | var data = JsonConvert.DeserializeObject>(responseData);
71 |
72 | return downloadUrl + data["downloadTokens"];
73 | }
74 | }
75 | catch (TaskCanceledException)
76 | {
77 | if (options.ThrowOnCancel)
78 | {
79 | throw;
80 | }
81 |
82 | return string.Empty;
83 | }
84 | catch (Exception ex)
85 | {
86 | throw new FirebaseStorageException(url, responseData, ex);
87 | }
88 | }
89 |
90 | private async void ReportProgressLoop()
91 | {
92 | while (!this.uploadTask.IsCompleted)
93 | {
94 | await Task.Delay(ProgressReportDelayMiliseconds);
95 |
96 | try
97 | {
98 | this.OnReportProgress(new FirebaseStorageProgress(this.stream.Position, this.stream.Length));
99 | }
100 | catch (ObjectDisposedException)
101 | {
102 | // there is no 100 % way to prevent ObjectDisposedException, there are bound to be concurrency issues.
103 | return;
104 | }
105 | }
106 | }
107 |
108 | private void OnReportProgress(FirebaseStorageProgress progress)
109 | {
110 | (this.Progress as IProgress).Report(progress);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Firebase.Storage/HttpClientFactory.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage
2 | {
3 | using System;
4 | using System.Net.Http;
5 | using System.Net.Http.Headers;
6 | using System.Threading.Tasks;
7 |
8 | public static class HttpClientFactory
9 | {
10 | ///
11 | /// Creates a new with authentication header when is specified.
12 | ///
13 | /// Firebase storage options.
14 | public static async Task CreateHttpClientAsync(this FirebaseStorageOptions options)
15 | {
16 | var client = new HttpClient();
17 |
18 | if (options.HttpClientTimeout != default(TimeSpan))
19 | {
20 | client.Timeout = options.HttpClientTimeout;
21 | }
22 |
23 | if (options.AuthTokenAsyncFactory != null)
24 | {
25 | var auth = await options.AuthTokenAsyncFactory().ConfigureAwait(false);
26 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Firebase", auth);
27 | }
28 |
29 | return client;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/test/Firebase.Storage.IntegrationTests/AuthenticationExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage.UnitTests
2 | {
3 | using Firebase.Auth;
4 | using Firebase.Storage.UnitTests.Models;
5 |
6 | internal static class AuthenticationExtensions
7 | {
8 | internal static FirebaseStorageOptions GetStorageAuthOptions(this FirebaseAuthProvider authProvider, FirebaseAuthenticationModel authSettings)
9 | {
10 | return new FirebaseStorageOptions { AuthTokenAsyncFactory = async () => await authSettings.GetAuthenticationTokenAsync(authProvider) };
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/test/Firebase.Storage.IntegrationTests/Firebase.Storage.IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | PreserveNewest
18 |
19 |
20 | PreserveNewest
21 |
22 |
23 | PreserveNewest
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/test/Firebase.Storage.IntegrationTests/Models/FirebaseAuthenticationModel.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage.UnitTests.Models
2 | {
3 | using System.Threading.Tasks;
4 |
5 | using Firebase.Auth;
6 |
7 | public sealed class FirebaseAuthenticationModel
8 | {
9 | public string ApiKey { get; set; }
10 |
11 | public string Email { get; set; }
12 |
13 | public string Password { get; set; }
14 |
15 | public FirebaseAuthProvider CreateAuthProvider()
16 | {
17 | return new FirebaseAuthProvider(new FirebaseConfig(this.ApiKey));
18 | }
19 |
20 | public async Task GetAuthenticationTokenAsync(FirebaseAuthProvider authProvider)
21 | {
22 | return (await authProvider.SignInWithEmailAndPasswordAsync(this.Email, this.Password)).FirebaseToken;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/test/Firebase.Storage.IntegrationTests/Models/Secrets.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage.UnitTests.Models
2 | {
3 | public sealed class Secrets
4 | {
5 | public string BucketName { get; set; }
6 |
7 | public FirebaseAuthenticationModel Authentication { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/test/Firebase.Storage.IntegrationTests/UploadWithMimeTypeTest.cs:
--------------------------------------------------------------------------------
1 | namespace Firebase.Storage.UnitTests
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | using Firebase.Storage.UnitTests.Models;
9 |
10 | using FluentAssertions;
11 |
12 | using Newtonsoft.Json;
13 |
14 | using Xunit;
15 |
16 | public sealed class UploadWithMimeTypeTest
17 | {
18 | [Theory]
19 | [InlineData("secrets.json", "test.jpg", "image/jpeg")]
20 | public async Task ShouldBeSetWhenDownloadingAsync(string secretsFilePath, string fileToUpload, string mimeType)
21 | {
22 | File.Exists(secretsFilePath).Should().BeTrue("You need to create your own secrets.json (check out the example file for details)");
23 |
24 | var secrets = JsonConvert.DeserializeObject(File.ReadAllText(secretsFilePath));
25 | var options = secrets.Authentication.CreateAuthProvider().GetStorageAuthOptions(secrets.Authentication);
26 | var storage = new FirebaseStorage(secrets.BucketName, options);
27 |
28 | long actualStreamSize;
29 | string downloadUrl;
30 | using (var contentStream = new MemoryStream(File.ReadAllBytes(fileToUpload)))
31 | {
32 | actualStreamSize = contentStream.Length;
33 | downloadUrl = await storage.Child("test.jpg").PutAsync(
34 | contentStream,
35 | CancellationToken.None,
36 | mimeType);
37 | }
38 |
39 | // now download the file and check the mime-type
40 | var http = await storage.Options.CreateHttpClientAsync();
41 | using (var response = await http.GetAsync(downloadUrl))
42 | {
43 | response.Content.Headers.ContentType.MediaType.Should().Be(mimeType);
44 | response.Content.Headers.ContentLength.HasValue.Should().BeTrue();
45 | // ReSharper disable once PossibleInvalidOperationException
46 | response.Content.Headers.ContentLength.Value.Should().Be(actualStreamSize);
47 | (await response.Content.ReadAsByteArrayAsync()).LongLength.Should().Be(actualStreamSize);
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/test/Firebase.Storage.IntegrationTests/secrets.json.example:
--------------------------------------------------------------------------------
1 | {
2 | "BucketName": "YOUR_PROJECT.appspot.com",
3 | "Authentication": {
4 | "ApiKey": "YOUR_API_KEY",
5 | "Email": "...",
6 | "Password": "..."
7 | }
8 | }
--------------------------------------------------------------------------------
/test/Firebase.Storage.IntegrationTests/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/step-up-labs/firebase-storage-dotnet/69dac6f45673d82db139fb6b0bd7b1256db164df/test/Firebase.Storage.IntegrationTests/test.jpg
--------------------------------------------------------------------------------
/test/Firebase.Storage.Tests.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2026
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Firebase.Storage", "..\src\Firebase.Storage\Firebase.Storage.csproj", "{626465CB-8713-4E30-9834-D273A3A0D1B1}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F3A8B0F0-FAE4-4F9F-BE3C-E0BCB9570145}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5876DFA6-9BB7-46F8-A8C0-A33A6DB7202A}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Firebase.Storage.IntegrationTests", "Firebase.Storage.IntegrationTests\Firebase.Storage.IntegrationTests.csproj", "{C67454BD-7262-4EA0-8147-75F1C5701E42}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {626465CB-8713-4E30-9834-D273A3A0D1B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {626465CB-8713-4E30-9834-D273A3A0D1B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {626465CB-8713-4E30-9834-D273A3A0D1B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {626465CB-8713-4E30-9834-D273A3A0D1B1}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {C67454BD-7262-4EA0-8147-75F1C5701E42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {C67454BD-7262-4EA0-8147-75F1C5701E42}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {C67454BD-7262-4EA0-8147-75F1C5701E42}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {C67454BD-7262-4EA0-8147-75F1C5701E42}.Release|Any CPU.Build.0 = Release|Any CPU
28 | EndGlobalSection
29 | GlobalSection(SolutionProperties) = preSolution
30 | HideSolutionNode = FALSE
31 | EndGlobalSection
32 | GlobalSection(NestedProjects) = preSolution
33 | {626465CB-8713-4E30-9834-D273A3A0D1B1} = {F3A8B0F0-FAE4-4F9F-BE3C-E0BCB9570145}
34 | {C67454BD-7262-4EA0-8147-75F1C5701E42} = {5876DFA6-9BB7-46F8-A8C0-A33A6DB7202A}
35 | EndGlobalSection
36 | GlobalSection(ExtensibilityGlobals) = postSolution
37 | SolutionGuid = {A78FBE32-9FF6-46B3-B797-BD566377BD72}
38 | EndGlobalSection
39 | EndGlobal
40 |
--------------------------------------------------------------------------------