Skip to content

Commit

Permalink
add ftp adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
mvdgun committed Jan 2, 2024
1 parent a1f0603 commit 40b8117
Show file tree
Hide file tree
Showing 30 changed files with 493 additions and 74 deletions.
3 changes: 2 additions & 1 deletion FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ public override void Dispose()
client.Dispose();
}

public override void Connect()
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
{
Logger.LogStartConnectingAdapter(this);
await Task.CompletedTask;
Logger.LogFinishedConnectingAdapter(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ public override void Dispose()
{
}

public override void Connect()
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
{
Logger.LogStartConnectingAdapter(this);
await Task.CompletedTask;
Logger.LogFinishedConnectingAdapter(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ public override void Dispose()
{
}

public override void Connect()
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
{
Logger.LogStartConnectingAdapter(this);
await Task.CompletedTask;
Logger.LogFinishedConnectingAdapter(this);
}

Expand Down
3 changes: 2 additions & 1 deletion FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ public override void Dispose()
client.Dispose();
}

public override void Connect()
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
{
Logger.LogStartConnectingAdapter(this);
await Task.CompletedTask;
Logger.LogFinishedConnectingAdapter(this);
}

Expand Down
31 changes: 31 additions & 0 deletions FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<RootNamespace>SharpGrip.FileSystem.Adapters.Ftp</RootNamespace>
</PropertyGroup>

<PropertyGroup>
<AssemblyName>SharpGrip.FileSystem.Adapters.Ftp</AssemblyName>
<PackageId>SharpGrip.FileSystem.Adapters.Ftp</PackageId>
<Title>SharpGrip FileSystem FTP adapter</Title>
<Description>The SharpGrip FileSystem FTP adapter.</Description>
<PackageTags>sharpgrip;file-system;ftp</PackageTags>
</PropertyGroup>

<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FluentFTP" Version="49.0.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FileSystem\FileSystem.csproj" />
</ItemGroup>

</Project>
253 changes: 253 additions & 0 deletions FileSystem.Adapters.Ftp/src/FtpAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using FluentFTP;
using FluentFTP.Exceptions;
using SharpGrip.FileSystem.Constants;
using SharpGrip.FileSystem.Exceptions;
using SharpGrip.FileSystem.Extensions;
using SharpGrip.FileSystem.Models;
using SharpGrip.FileSystem.Utilities;
using DirectoryNotFoundException = SharpGrip.FileSystem.Exceptions.DirectoryNotFoundException;
using FileNotFoundException = SharpGrip.FileSystem.Exceptions.FileNotFoundException;

namespace SharpGrip.FileSystem.Adapters.Ftp
{
public class FtpAdapter : Adapter<FtpAdapterConfiguration, string, string>
{
private readonly IAsyncFtpClient client;

public FtpAdapter(string prefix, string rootPath, IAsyncFtpClient client, Action<FtpAdapterConfiguration>? configuration = null) : base(prefix, rootPath, configuration)
{
this.client = client;
}

public override void Dispose()
{
client.Dispose();
}

public override async Task ConnectAsync(CancellationToken cancellationToken = default)
{
if (client.IsConnected)
{
return;
}

try
{
Logger.LogStartConnectingAdapter(this);
await client.Connect(cancellationToken);
Logger.LogFinishedConnectingAdapter(this);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task<IFile> GetFileAsync(string virtualPath, CancellationToken cancellationToken = default)
{
var path = GetPath(virtualPath);

try
{
var file = await client.GetObjectInfo(path, token: cancellationToken);

if (file == null || file.Type != FtpObjectType.File)
{
throw new FileNotFoundException(path, Prefix);
}

return ModelFactory.CreateFile(file, virtualPath);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task<IDirectory> GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default)
{
var path = GetPath(virtualPath);

try
{
var directory = await client.GetObjectInfo(path, token: cancellationToken);

if (directory == null || directory.Type != FtpObjectType.Directory)
{
throw new DirectoryNotFoundException(path, Prefix);
}

return ModelFactory.CreateDirectory(directory, virtualPath);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task<IEnumerable<IFile>> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default)
{
await GetDirectoryAsync(virtualPath, cancellationToken);
var path = GetPath(virtualPath);

try
{
var ftpListItems = await client.GetListing(path, cancellationToken);

return ftpListItems.Where(file => file.Type == FtpObjectType.File).Select(file => ModelFactory.CreateFile(file, GetVirtualPath(file.FullName)));
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task<IEnumerable<IDirectory>> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default)
{
await GetDirectoryAsync(virtualPath, cancellationToken);
var path = GetPath(virtualPath);

try
{
var ftpListItems = await client.GetListing(path, cancellationToken);

return ftpListItems.Where(file => file.Type == FtpObjectType.Directory).Select(file => ModelFactory.CreateDirectory(file, GetVirtualPath(file.FullName)));
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task CreateDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default)
{
if (await DirectoryExistsAsync(virtualPath, cancellationToken))
{
throw new DirectoryExistsException(GetPath(virtualPath), Prefix);
}

try
{
await client.CreateDirectory(GetPath(virtualPath), cancellationToken);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default)
{
await GetDirectoryAsync(virtualPath, cancellationToken);

try
{
await client.DeleteDirectory(GetPath(virtualPath), cancellationToken);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default)
{
await GetFileAsync(virtualPath, cancellationToken);

try
{
await client.DeleteFile(GetPath(virtualPath), cancellationToken);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task<Stream> ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default)
{
await GetFileAsync(virtualPath, cancellationToken);

try
{
var fileStream = await client.OpenRead(GetPath(virtualPath), token: cancellationToken);

return await StreamUtilities.CopyContentsToMemoryStreamAsync(fileStream, true, cancellationToken);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default)
{
if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken))
{
throw new FileExistsException(GetPath(virtualPath), Prefix);
}

try
{
contents.Seek(0, SeekOrigin.Begin);

using var writeStream = await client.OpenWrite(GetPath(virtualPath), token: cancellationToken);

await contents.CopyToAsync(writeStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken);
await writeStream.FlushAsync(cancellationToken);
}
catch (Exception exception)
{
throw Exception(exception);
}
}

public override async Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default)
{
await GetFileAsync(virtualPath, cancellationToken);

try
{
using var fileStream = await client.OpenAppend(GetPath(virtualPath), token: cancellationToken);

await contents.CopyToAsync(fileStream);
}
catch (Exception exception)
{
throw new AdapterRuntimeException(exception);
}
}

protected override Exception Exception(Exception exception)
{
if (exception is FileSystemException)
{
return exception;
}

if (exception is SocketException socketException)
{
return new ConnectionException(socketException);
}

if (exception is FtpAuthenticationException ftpAuthenticationException)
{
return new ConnectionException(ftpAuthenticationException);
}

if (exception is FtpSecurityNotAvailableException ftpSecurityNotAvailableException)
{
return new ConnectionException(ftpSecurityNotAvailableException);
}

return new AdapterRuntimeException(exception);
}
}
}
8 changes: 8 additions & 0 deletions FileSystem.Adapters.Ftp/src/FtpAdapterConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using SharpGrip.FileSystem.Configuration;

namespace SharpGrip.FileSystem.Adapters.Ftp
{
public class FtpAdapterConfiguration : AdapterConfiguration
{
}
}
33 changes: 33 additions & 0 deletions FileSystem.Adapters.Ftp/src/ModelFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using FluentFTP;
using SharpGrip.FileSystem.Models;

namespace SharpGrip.FileSystem.Adapters.Ftp
{
public static class ModelFactory
{
public static IFile CreateFile(FtpListItem file, string virtualPath)
{
return new FileModel
{
Name = file.Name,
Path = file.FullName,
VirtualPath = virtualPath,
Length = file.Size,
LastModifiedDateTime = file.Modified,
CreatedDateTime = file.Created
};
}

public static DirectoryModel CreateDirectory(FtpListItem directory, string virtualPath)
{
return new DirectoryModel
{
Name = directory.Name,
Path = directory.FullName,
VirtualPath = virtualPath,
LastModifiedDateTime = directory.Modified,
CreatedDateTime = directory.Created
};
}
}
}
3 changes: 2 additions & 1 deletion FileSystem.Adapters.GoogleDrive/src/GoogleDriveAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ public override void Dispose()
client.Dispose();
}

public override void Connect()
public override async Task ConnectAsync(CancellationToken cancellationToken = default)
{
Logger.LogStartConnectingAdapter(this);
await Task.CompletedTask;
Logger.LogFinishedConnectingAdapter(this);
}

Expand Down
Loading

0 comments on commit 40b8117

Please sign in to comment.