1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . IO ;
4+ using System . Linq ;
5+ using System . Net ;
6+ using System . Threading ;
7+ using System . Threading . Tasks ;
8+ using Google ;
9+ using Google . Cloud . Storage . V1 ;
10+ using SharpGrip . FileSystem . Exceptions ;
11+ using SharpGrip . FileSystem . Extensions ;
12+ using SharpGrip . FileSystem . Models ;
13+ using SharpGrip . FileSystem . Utilities ;
14+ using DirectoryNotFoundException = SharpGrip . FileSystem . Exceptions . DirectoryNotFoundException ;
15+ using FileNotFoundException = SharpGrip . FileSystem . Exceptions . FileNotFoundException ;
16+ using Object = Google . Apis . Storage . v1 . Data . Object ;
17+
18+ namespace SharpGrip . FileSystem . Adapters . GoogleCloudStorage
19+ {
20+ public class GoogleCloudStorageAdapter : Adapter < GoogleCloudStorageAdapterConfiguration , string , string >
21+ {
22+ private readonly StorageClient client ;
23+ private readonly string bucketName ;
24+
25+ public GoogleCloudStorageAdapter ( string prefix , string rootPath , StorageClient client , string bucketName , Action < GoogleCloudStorageAdapterConfiguration > ? configuration = null )
26+ : base ( prefix , rootPath , configuration )
27+ {
28+ this . client = client ;
29+ this . bucketName = bucketName ;
30+ }
31+
32+ public override void Dispose ( )
33+ {
34+ client . Dispose ( ) ;
35+ }
36+
37+ public override async Task ConnectAsync ( CancellationToken cancellationToken = default )
38+ {
39+ Logger . LogStartConnectingAdapter ( this ) ;
40+ await Task . CompletedTask ;
41+ Logger . LogFinishedConnectingAdapter ( this ) ;
42+ }
43+
44+ public override async Task < IFile > GetFileAsync ( string virtualPath , CancellationToken cancellationToken = default )
45+ {
46+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . RemoveTrailingForwardSlash ( ) ;
47+
48+ try
49+ {
50+ var file = await client . GetObjectAsync ( bucketName , path , new GetObjectOptions ( ) , cancellationToken ) ;
51+
52+ if ( file == null )
53+ {
54+ throw new FileNotFoundException ( path , Prefix ) ;
55+ }
56+
57+ return ModelFactory . CreateFile ( file , path , virtualPath ) ;
58+ }
59+ catch ( GoogleApiException googleApiException ) when ( googleApiException . HttpStatusCode == HttpStatusCode . NotFound )
60+ {
61+ throw new FileNotFoundException ( path , Prefix ) ;
62+ }
63+ catch ( Exception exception )
64+ {
65+ throw Exception ( exception ) ;
66+ }
67+ }
68+
69+ public override async Task < IDirectory > GetDirectoryAsync ( string virtualPath , CancellationToken cancellationToken = default )
70+ {
71+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . EnsureTrailingForwardSlash ( ) ;
72+ var parentPath = GetParentPathPart ( path ) . EnsureTrailingForwardSlash ( ) ;
73+
74+ try
75+ {
76+ if ( path . IsNullOrEmpty ( ) || path == "/" )
77+ {
78+ return ModelFactory . CreateDirectory ( "/" , path , virtualPath ) ;
79+ }
80+
81+ var request = client . Service . Objects . List ( bucketName ) ;
82+
83+ request . Prefix = parentPath == "/" ? null : parentPath ;
84+ request . Delimiter = "/" ;
85+
86+ do
87+ {
88+ var objects = await request . ExecuteAsync ( cancellationToken : cancellationToken ) ;
89+
90+ if ( objects . Prefixes != null )
91+ {
92+ foreach ( var directoryPath in objects . Prefixes )
93+ {
94+ if ( directoryPath == path )
95+ {
96+ var directoryName = GetLastPathPart ( directoryPath ) ;
97+
98+ return ModelFactory . CreateDirectory ( directoryName . RemoveTrailingForwardSlash ( ) , directoryPath . EnsureTrailingForwardSlash ( ) , GetVirtualPath ( directoryPath ) ) ;
99+ }
100+ }
101+ }
102+
103+ request . PageToken = objects . NextPageToken ;
104+ } while ( request . PageToken != null ) ;
105+
106+ throw new DirectoryNotFoundException ( path , Prefix ) ;
107+ }
108+ catch ( GoogleApiException googleApiException ) when ( googleApiException . HttpStatusCode == HttpStatusCode . NotFound )
109+ {
110+ throw new DirectoryNotFoundException ( path , Prefix ) ;
111+ }
112+ catch ( Exception exception )
113+ {
114+ throw Exception ( exception ) ;
115+ }
116+ }
117+
118+ public override async Task < IEnumerable < IFile > > GetFilesAsync ( string virtualPath = "" , CancellationToken cancellationToken = default )
119+ {
120+ await GetDirectoryAsync ( virtualPath , cancellationToken ) ;
121+
122+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . EnsureTrailingForwardSlash ( ) ;
123+
124+ try
125+ {
126+ var files = new List < IFile > ( ) ;
127+
128+ var request = client . Service . Objects . List ( bucketName ) ;
129+
130+ request . Prefix = path == "/" ? null : path ;
131+ request . Delimiter = "/" ;
132+
133+ do
134+ {
135+ var objects = await request . ExecuteAsync ( cancellationToken : cancellationToken ) ;
136+
137+ if ( objects . Items != null )
138+ {
139+ foreach ( var file in objects . Items . Where ( item => item . ContentType != null ) )
140+ {
141+ files . Add ( ModelFactory . CreateFile ( file , file . Name . RemoveTrailingForwardSlash ( ) , GetVirtualPath ( file . Name ) ) ) ;
142+ }
143+ }
144+
145+ request . PageToken = objects . NextPageToken ;
146+ } while ( request . PageToken != null ) ;
147+
148+ return files ;
149+ }
150+ catch ( Exception exception )
151+ {
152+ throw Exception ( exception ) ;
153+ }
154+ }
155+
156+ public override async Task < IEnumerable < IDirectory > > GetDirectoriesAsync ( string virtualPath = "" , CancellationToken cancellationToken = default )
157+ {
158+ await GetDirectoryAsync ( virtualPath , cancellationToken ) ;
159+
160+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . EnsureTrailingForwardSlash ( ) ;
161+
162+ try
163+ {
164+ var directories = new List < IDirectory > ( ) ;
165+
166+ var request = client . Service . Objects . List ( bucketName ) ;
167+
168+ request . Prefix = path == "/" ? null : path ;
169+ request . Delimiter = "/" ;
170+
171+ do
172+ {
173+ var objects = await request . ExecuteAsync ( cancellationToken : cancellationToken ) ;
174+
175+ if ( objects . Prefixes != null )
176+ {
177+ foreach ( var directoryPath in objects . Prefixes )
178+ {
179+ var directoryName = GetLastPathPart ( directoryPath ) ;
180+
181+ directories . Add ( ModelFactory . CreateDirectory ( directoryName . RemoveTrailingForwardSlash ( ) , directoryPath . EnsureTrailingForwardSlash ( ) , GetVirtualPath ( directoryPath ) ) ) ;
182+ }
183+ }
184+
185+ request . PageToken = objects . NextPageToken ;
186+ } while ( request . PageToken != null ) ;
187+
188+ return directories ;
189+ }
190+ catch ( Exception exception )
191+ {
192+ throw Exception ( exception ) ;
193+ }
194+ }
195+
196+ public override async Task CreateDirectoryAsync ( string virtualPath , CancellationToken cancellationToken = default )
197+ {
198+ if ( await DirectoryExistsAsync ( virtualPath , cancellationToken ) )
199+ {
200+ throw new DirectoryExistsException ( GetPath ( virtualPath ) , Prefix ) ;
201+ }
202+
203+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . EnsureTrailingForwardSlash ( ) ;
204+
205+ try
206+ {
207+ await client . UploadObjectAsync ( bucketName , path . EnsureTrailingForwardSlash ( ) , null , Stream . Null , cancellationToken : cancellationToken ) ;
208+ }
209+ catch ( Exception exception )
210+ {
211+ throw Exception ( exception ) ;
212+ }
213+ }
214+
215+ public override async Task DeleteDirectoryAsync ( string virtualPath , CancellationToken cancellationToken = default )
216+ {
217+ await GetDirectoryAsync ( virtualPath , cancellationToken ) ;
218+
219+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . EnsureTrailingForwardSlash ( ) ;
220+
221+ try
222+ {
223+ var files = await GetFilesAsync ( virtualPath , cancellationToken ) ;
224+
225+ foreach ( var file in files )
226+ {
227+ await DeleteFileAsync ( file . VirtualPath , cancellationToken ) ;
228+ }
229+
230+ var subDirectories = await GetDirectoriesAsync ( virtualPath , cancellationToken ) ;
231+
232+ foreach ( var subDirectory in subDirectories )
233+ {
234+ await DeleteDirectoryAsync ( subDirectory . VirtualPath , cancellationToken ) ;
235+ }
236+
237+ await client . DeleteObjectAsync ( bucketName , path , cancellationToken : cancellationToken ) ;
238+ }
239+ catch ( Exception exception )
240+ {
241+ throw Exception ( exception ) ;
242+ }
243+ }
244+
245+ public override async Task DeleteFileAsync ( string virtualPath , CancellationToken cancellationToken = default )
246+ {
247+ await GetFileAsync ( virtualPath , cancellationToken ) ;
248+
249+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . RemoveTrailingForwardSlash ( ) ;
250+
251+ try
252+ {
253+ await client . DeleteObjectAsync ( bucketName , path , cancellationToken : cancellationToken ) ;
254+ }
255+ catch ( Exception exception )
256+ {
257+ throw Exception ( exception ) ;
258+ }
259+ }
260+
261+ public override async Task < Stream > ReadFileStreamAsync ( string virtualPath , CancellationToken cancellationToken = default )
262+ {
263+ await GetFileAsync ( virtualPath , cancellationToken ) ;
264+
265+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . RemoveTrailingForwardSlash ( ) ;
266+
267+ try
268+ {
269+ var file = await client . GetObjectAsync ( bucketName , path , new GetObjectOptions ( ) , cancellationToken ) ;
270+
271+ var memoryStream = new MemoryStream ( ) ;
272+
273+ await client . DownloadObjectAsync ( file , memoryStream , new DownloadObjectOptions ( ) , cancellationToken ) ;
274+
275+ memoryStream . Position = 0 ;
276+
277+ return memoryStream ;
278+ }
279+ catch ( Exception exception )
280+ {
281+ throw Exception ( exception ) ;
282+ }
283+ }
284+
285+ public override async Task WriteFileAsync ( string virtualPath , Stream contents , bool overwrite = false , CancellationToken cancellationToken = default )
286+ {
287+ if ( ! overwrite && await FileExistsAsync ( virtualPath , cancellationToken ) )
288+ {
289+ throw new FileExistsException ( GetPath ( virtualPath ) , Prefix ) ;
290+ }
291+
292+ var path = GetPath ( virtualPath ) . RemoveLeadingForwardSlash ( ) . RemoveTrailingForwardSlash ( ) ;
293+
294+ try
295+ {
296+ var file = new Object
297+ {
298+ Bucket = bucketName ,
299+ Name = path ,
300+ ContentType = ContentTypeProvider . GetContentType ( path )
301+ } ;
302+
303+ await client . UploadObjectAsync ( file , contents , new UploadObjectOptions ( ) , cancellationToken ) ;
304+ }
305+ catch ( Exception exception )
306+ {
307+ throw Exception ( exception ) ;
308+ }
309+ }
310+
311+ protected override Exception Exception ( Exception exception )
312+ {
313+ if ( exception is FileSystemException )
314+ {
315+ return exception ;
316+ }
317+
318+ return new AdapterRuntimeException ( exception ) ;
319+ }
320+ }
321+ }
0 commit comments