Skip to content

Commit 7867b7f

Browse files
authored
Merge pull request #38 from SharpGrip/feature/new-adapter-ftp
add ftp adapter
2 parents a1f0603 + 40b8117 commit 7867b7f

30 files changed

Lines changed: 493 additions & 74 deletions

File tree

FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ public override void Dispose()
3131
client.Dispose();
3232
}
3333

34-
public override void Connect()
34+
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
3535
{
3636
Logger.LogStartConnectingAdapter(this);
37+
await Task.CompletedTask;
3738
Logger.LogFinishedConnectingAdapter(this);
3839
}
3940

FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ public override void Dispose()
2828
{
2929
}
3030

31-
public override void Connect()
31+
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
3232
{
3333
Logger.LogStartConnectingAdapter(this);
34+
await Task.CompletedTask;
3435
Logger.LogFinishedConnectingAdapter(this);
3536
}
3637

FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ public override void Dispose()
2727
{
2828
}
2929

30-
public override void Connect()
30+
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
3131
{
3232
Logger.LogStartConnectingAdapter(this);
33+
await Task.CompletedTask;
3334
Logger.LogFinishedConnectingAdapter(this);
3435
}
3536

FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ public override void Dispose()
2929
client.Dispose();
3030
}
3131

32-
public override void Connect()
32+
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
3333
{
3434
Logger.LogStartConnectingAdapter(this);
35+
await Task.CompletedTask;
3536
Logger.LogFinishedConnectingAdapter(this);
3637
}
3738

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<RootNamespace>SharpGrip.FileSystem.Adapters.Ftp</RootNamespace>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<AssemblyName>SharpGrip.FileSystem.Adapters.Ftp</AssemblyName>
9+
<PackageId>SharpGrip.FileSystem.Adapters.Ftp</PackageId>
10+
<Title>SharpGrip FileSystem FTP adapter</Title>
11+
<Description>The SharpGrip FileSystem FTP adapter.</Description>
12+
<PackageTags>sharpgrip;file-system;ftp</PackageTags>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<None Include="..\README.md" Pack="true" PackagePath="\" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<PackageReference Include="FluentFTP" Version="49.0.1" />
21+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
22+
<PrivateAssets>all</PrivateAssets>
23+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
24+
</PackageReference>
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<ProjectReference Include="..\FileSystem\FileSystem.csproj" />
29+
</ItemGroup>
30+
31+
</Project>
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Net.Sockets;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using FluentFTP;
9+
using FluentFTP.Exceptions;
10+
using SharpGrip.FileSystem.Constants;
11+
using SharpGrip.FileSystem.Exceptions;
12+
using SharpGrip.FileSystem.Extensions;
13+
using SharpGrip.FileSystem.Models;
14+
using SharpGrip.FileSystem.Utilities;
15+
using DirectoryNotFoundException = SharpGrip.FileSystem.Exceptions.DirectoryNotFoundException;
16+
using FileNotFoundException = SharpGrip.FileSystem.Exceptions.FileNotFoundException;
17+
18+
namespace SharpGrip.FileSystem.Adapters.Ftp
19+
{
20+
public class FtpAdapter : Adapter<FtpAdapterConfiguration, string, string>
21+
{
22+
private readonly IAsyncFtpClient client;
23+
24+
public FtpAdapter(string prefix, string rootPath, IAsyncFtpClient client, Action<FtpAdapterConfiguration>? configuration = null) : base(prefix, rootPath, configuration)
25+
{
26+
this.client = client;
27+
}
28+
29+
public override void Dispose()
30+
{
31+
client.Dispose();
32+
}
33+
34+
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
35+
{
36+
if (client.IsConnected)
37+
{
38+
return;
39+
}
40+
41+
try
42+
{
43+
Logger.LogStartConnectingAdapter(this);
44+
await client.Connect(cancellationToken);
45+
Logger.LogFinishedConnectingAdapter(this);
46+
}
47+
catch (Exception exception)
48+
{
49+
throw Exception(exception);
50+
}
51+
}
52+
53+
public override async Task<IFile> GetFileAsync(string virtualPath, CancellationToken cancellationToken = default)
54+
{
55+
var path = GetPath(virtualPath);
56+
57+
try
58+
{
59+
var file = await client.GetObjectInfo(path, token: cancellationToken);
60+
61+
if (file == null || file.Type != FtpObjectType.File)
62+
{
63+
throw new FileNotFoundException(path, Prefix);
64+
}
65+
66+
return ModelFactory.CreateFile(file, virtualPath);
67+
}
68+
catch (Exception exception)
69+
{
70+
throw Exception(exception);
71+
}
72+
}
73+
74+
public override async Task<IDirectory> GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default)
75+
{
76+
var path = GetPath(virtualPath);
77+
78+
try
79+
{
80+
var directory = await client.GetObjectInfo(path, token: cancellationToken);
81+
82+
if (directory == null || directory.Type != FtpObjectType.Directory)
83+
{
84+
throw new DirectoryNotFoundException(path, Prefix);
85+
}
86+
87+
return ModelFactory.CreateDirectory(directory, virtualPath);
88+
}
89+
catch (Exception exception)
90+
{
91+
throw Exception(exception);
92+
}
93+
}
94+
95+
public override async Task<IEnumerable<IFile>> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default)
96+
{
97+
await GetDirectoryAsync(virtualPath, cancellationToken);
98+
var path = GetPath(virtualPath);
99+
100+
try
101+
{
102+
var ftpListItems = await client.GetListing(path, cancellationToken);
103+
104+
return ftpListItems.Where(file => file.Type == FtpObjectType.File).Select(file => ModelFactory.CreateFile(file, GetVirtualPath(file.FullName)));
105+
}
106+
catch (Exception exception)
107+
{
108+
throw Exception(exception);
109+
}
110+
}
111+
112+
public override async Task<IEnumerable<IDirectory>> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default)
113+
{
114+
await GetDirectoryAsync(virtualPath, cancellationToken);
115+
var path = GetPath(virtualPath);
116+
117+
try
118+
{
119+
var ftpListItems = await client.GetListing(path, cancellationToken);
120+
121+
return ftpListItems.Where(file => file.Type == FtpObjectType.Directory).Select(file => ModelFactory.CreateDirectory(file, GetVirtualPath(file.FullName)));
122+
}
123+
catch (Exception exception)
124+
{
125+
throw Exception(exception);
126+
}
127+
}
128+
129+
public override async Task CreateDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default)
130+
{
131+
if (await DirectoryExistsAsync(virtualPath, cancellationToken))
132+
{
133+
throw new DirectoryExistsException(GetPath(virtualPath), Prefix);
134+
}
135+
136+
try
137+
{
138+
await client.CreateDirectory(GetPath(virtualPath), cancellationToken);
139+
}
140+
catch (Exception exception)
141+
{
142+
throw Exception(exception);
143+
}
144+
}
145+
146+
public override async Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default)
147+
{
148+
await GetDirectoryAsync(virtualPath, cancellationToken);
149+
150+
try
151+
{
152+
await client.DeleteDirectory(GetPath(virtualPath), cancellationToken);
153+
}
154+
catch (Exception exception)
155+
{
156+
throw Exception(exception);
157+
}
158+
}
159+
160+
public override async Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default)
161+
{
162+
await GetFileAsync(virtualPath, cancellationToken);
163+
164+
try
165+
{
166+
await client.DeleteFile(GetPath(virtualPath), cancellationToken);
167+
}
168+
catch (Exception exception)
169+
{
170+
throw Exception(exception);
171+
}
172+
}
173+
174+
public override async Task<Stream> ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default)
175+
{
176+
await GetFileAsync(virtualPath, cancellationToken);
177+
178+
try
179+
{
180+
var fileStream = await client.OpenRead(GetPath(virtualPath), token: cancellationToken);
181+
182+
return await StreamUtilities.CopyContentsToMemoryStreamAsync(fileStream, true, cancellationToken);
183+
}
184+
catch (Exception exception)
185+
{
186+
throw Exception(exception);
187+
}
188+
}
189+
190+
public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default)
191+
{
192+
if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken))
193+
{
194+
throw new FileExistsException(GetPath(virtualPath), Prefix);
195+
}
196+
197+
try
198+
{
199+
contents.Seek(0, SeekOrigin.Begin);
200+
201+
using var writeStream = await client.OpenWrite(GetPath(virtualPath), token: cancellationToken);
202+
203+
await contents.CopyToAsync(writeStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken);
204+
await writeStream.FlushAsync(cancellationToken);
205+
}
206+
catch (Exception exception)
207+
{
208+
throw Exception(exception);
209+
}
210+
}
211+
212+
public override async Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default)
213+
{
214+
await GetFileAsync(virtualPath, cancellationToken);
215+
216+
try
217+
{
218+
using var fileStream = await client.OpenAppend(GetPath(virtualPath), token: cancellationToken);
219+
220+
await contents.CopyToAsync(fileStream);
221+
}
222+
catch (Exception exception)
223+
{
224+
throw new AdapterRuntimeException(exception);
225+
}
226+
}
227+
228+
protected override Exception Exception(Exception exception)
229+
{
230+
if (exception is FileSystemException)
231+
{
232+
return exception;
233+
}
234+
235+
if (exception is SocketException socketException)
236+
{
237+
return new ConnectionException(socketException);
238+
}
239+
240+
if (exception is FtpAuthenticationException ftpAuthenticationException)
241+
{
242+
return new ConnectionException(ftpAuthenticationException);
243+
}
244+
245+
if (exception is FtpSecurityNotAvailableException ftpSecurityNotAvailableException)
246+
{
247+
return new ConnectionException(ftpSecurityNotAvailableException);
248+
}
249+
250+
return new AdapterRuntimeException(exception);
251+
}
252+
}
253+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using SharpGrip.FileSystem.Configuration;
2+
3+
namespace SharpGrip.FileSystem.Adapters.Ftp
4+
{
5+
public class FtpAdapterConfiguration : AdapterConfiguration
6+
{
7+
}
8+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using FluentFTP;
2+
using SharpGrip.FileSystem.Models;
3+
4+
namespace SharpGrip.FileSystem.Adapters.Ftp
5+
{
6+
public static class ModelFactory
7+
{
8+
public static IFile CreateFile(FtpListItem file, string virtualPath)
9+
{
10+
return new FileModel
11+
{
12+
Name = file.Name,
13+
Path = file.FullName,
14+
VirtualPath = virtualPath,
15+
Length = file.Size,
16+
LastModifiedDateTime = file.Modified,
17+
CreatedDateTime = file.Created
18+
};
19+
}
20+
21+
public static DirectoryModel CreateDirectory(FtpListItem directory, string virtualPath)
22+
{
23+
return new DirectoryModel
24+
{
25+
Name = directory.Name,
26+
Path = directory.FullName,
27+
VirtualPath = virtualPath,
28+
LastModifiedDateTime = directory.Modified,
29+
CreatedDateTime = directory.Created
30+
};
31+
}
32+
}
33+
}

FileSystem.Adapters.GoogleDrive/src/GoogleDriveAdapter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ public override void Dispose()
4040
client.Dispose();
4141
}
4242

43-
public override void Connect()
43+
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
4444
{
4545
Logger.LogStartConnectingAdapter(this);
46+
await Task.CompletedTask;
4647
Logger.LogFinishedConnectingAdapter(this);
4748
}
4849

0 commit comments

Comments
 (0)