Skip to content

Commit 942a058

Browse files
committed
add onedrive google drive and dropbox providers
1 parent 68a37b5 commit 942a058

29 files changed

+3751
-0
lines changed

ManagedCode.Storage.slnx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
<Project Path="Storages/ManagedCode.Storage.FileSystem/ManagedCode.Storage.FileSystem.csproj" />
1717
<Project Path="Storages/ManagedCode.Storage.Sftp/ManagedCode.Storage.Sftp.csproj" />
1818
<Project Path="Storages/ManagedCode.Storage.Google/ManagedCode.Storage.Google.csproj" />
19+
<Project Path="Storages/ManagedCode.Storage.GoogleDrive/ManagedCode.Storage.GoogleDrive.csproj" />
20+
<Project Path="Storages/ManagedCode.Storage.OneDrive/ManagedCode.Storage.OneDrive.csproj" />
21+
<Project Path="Storages/ManagedCode.Storage.Dropbox/ManagedCode.Storage.Dropbox.csproj" />
1922
</Folder>
2023
<Folder Name="/Tests/">
2124
<Project Path="Tests/ManagedCode.Storage.Tests/ManagedCode.Storage.Tests.csproj" />
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.CompilerServices;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Dropbox.Api;
9+
using Dropbox.Api.Files;
10+
11+
namespace ManagedCode.Storage.Dropbox.Clients;
12+
13+
public class DropboxClientWrapper : IDropboxClientWrapper
14+
{
15+
private readonly DropboxClient _client;
16+
17+
public DropboxClientWrapper(DropboxClient client)
18+
{
19+
_client = client ?? throw new ArgumentNullException(nameof(client));
20+
}
21+
22+
public async Task EnsureRootAsync(string rootPath, bool createIfNotExists, CancellationToken cancellationToken)
23+
{
24+
if (string.IsNullOrWhiteSpace(rootPath))
25+
{
26+
return;
27+
}
28+
29+
var normalized = Normalize(rootPath);
30+
try
31+
{
32+
await _client.Files.GetMetadataAsync(normalized);
33+
}
34+
catch (ApiException<GetMetadataError> ex) when (ex.ErrorResponse.IsPath && ex.ErrorResponse.AsPath.Value.IsNotFound)
35+
{
36+
if (!createIfNotExists)
37+
{
38+
return;
39+
}
40+
41+
await _client.Files.CreateFolderV2Async(normalized, autorename: false);
42+
}
43+
}
44+
45+
public async Task<DropboxItemMetadata> UploadAsync(string rootPath, string path, Stream content, string? contentType, CancellationToken cancellationToken)
46+
{
47+
var fullPath = Combine(rootPath, path);
48+
var uploaded = await _client.Files.UploadAsync(fullPath, WriteMode.Overwrite.Instance, body: content);
49+
var metadata = (await _client.Files.GetMetadataAsync(uploaded.PathLower)).AsFile;
50+
return ToItem(metadata);
51+
}
52+
53+
public async Task<Stream> DownloadAsync(string rootPath, string path, CancellationToken cancellationToken)
54+
{
55+
var fullPath = Combine(rootPath, path);
56+
var response = await _client.Files.DownloadAsync(fullPath);
57+
return await response.GetContentAsStreamAsync();
58+
}
59+
60+
public async Task<bool> DeleteAsync(string rootPath, string path, CancellationToken cancellationToken)
61+
{
62+
var fullPath = Combine(rootPath, path);
63+
await _client.Files.DeleteV2Async(fullPath);
64+
return true;
65+
}
66+
67+
public async Task<bool> ExistsAsync(string rootPath, string path, CancellationToken cancellationToken)
68+
{
69+
var fullPath = Combine(rootPath, path);
70+
try
71+
{
72+
await _client.Files.GetMetadataAsync(fullPath);
73+
return true;
74+
}
75+
catch (ApiException<GetMetadataError> ex) when (ex.ErrorResponse.IsPath && ex.ErrorResponse.AsPath.Value.IsNotFound)
76+
{
77+
return false;
78+
}
79+
}
80+
81+
public async Task<DropboxItemMetadata?> GetMetadataAsync(string rootPath, string path, CancellationToken cancellationToken)
82+
{
83+
var fullPath = Combine(rootPath, path);
84+
try
85+
{
86+
var metadata = await _client.Files.GetMetadataAsync(fullPath);
87+
return metadata.IsFile ? ToItem(metadata.AsFile) : null;
88+
}
89+
catch (ApiException<GetMetadataError> ex) when (ex.ErrorResponse.IsPath && ex.ErrorResponse.AsPath.Value.IsNotFound)
90+
{
91+
return null;
92+
}
93+
}
94+
95+
public async IAsyncEnumerable<DropboxItemMetadata> ListAsync(string rootPath, string? directory, [EnumeratorCancellation] CancellationToken cancellationToken)
96+
{
97+
var fullPath = Combine(rootPath, directory ?? string.Empty);
98+
var list = await _client.Files.ListFolderAsync(fullPath);
99+
foreach (var item in list.Entries)
100+
{
101+
if (item.IsFile)
102+
{
103+
yield return ToItem(item.AsFile);
104+
}
105+
}
106+
107+
while (list.HasMore)
108+
{
109+
list = await _client.Files.ListFolderContinueAsync(list.Cursor);
110+
foreach (var item in list.Entries)
111+
{
112+
if (item.IsFile)
113+
{
114+
yield return ToItem(item.AsFile);
115+
}
116+
}
117+
}
118+
}
119+
120+
private static DropboxItemMetadata ToItem(FileMetadata file)
121+
{
122+
return new DropboxItemMetadata
123+
{
124+
Name = file.Name,
125+
Path = file.PathLower ?? file.PathDisplay ?? string.Empty,
126+
Size = file.Size,
127+
ClientModified = file.ClientModified,
128+
ServerModified = file.ServerModified
129+
};
130+
}
131+
132+
private static string Normalize(string path)
133+
{
134+
var normalized = path.Replace("\\", "/");
135+
if (!normalized.StartsWith('/'))
136+
{
137+
normalized = "/" + normalized;
138+
}
139+
140+
return normalized.TrimEnd('/') == string.Empty ? "/" : normalized.TrimEnd('/');
141+
}
142+
143+
private static string Combine(string root, string path)
144+
{
145+
var normalizedRoot = Normalize(root);
146+
var normalizedPath = path.Replace("\\", "/").Trim('/');
147+
if (string.IsNullOrWhiteSpace(normalizedPath))
148+
{
149+
return normalizedRoot;
150+
}
151+
152+
return normalizedRoot.EndsWith("/") ? normalizedRoot + normalizedPath : normalizedRoot + "/" + normalizedPath;
153+
}
154+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace ManagedCode.Storage.Dropbox.Clients;
4+
5+
public class DropboxItemMetadata
6+
{
7+
public required string Name { get; set; }
8+
public required string Path { get; set; }
9+
public ulong Size { get; set; }
10+
public DateTime ClientModified { get; set; }
11+
public DateTime ServerModified { get; set; }
12+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Dropbox.Api.Files;
6+
7+
namespace ManagedCode.Storage.Dropbox.Clients;
8+
9+
public interface IDropboxClientWrapper
10+
{
11+
Task EnsureRootAsync(string rootPath, bool createIfNotExists, CancellationToken cancellationToken);
12+
13+
Task<DropboxItemMetadata> UploadAsync(string rootPath, string path, Stream content, string? contentType, CancellationToken cancellationToken);
14+
15+
Task<Stream> DownloadAsync(string rootPath, string path, CancellationToken cancellationToken);
16+
17+
Task<bool> DeleteAsync(string rootPath, string path, CancellationToken cancellationToken);
18+
19+
Task<bool> ExistsAsync(string rootPath, string path, CancellationToken cancellationToken);
20+
21+
Task<DropboxItemMetadata?> GetMetadataAsync(string rootPath, string path, CancellationToken cancellationToken);
22+
23+
IAsyncEnumerable<DropboxItemMetadata> ListAsync(string rootPath, string? directory, CancellationToken cancellationToken);
24+
}

0 commit comments

Comments
 (0)