Skip to content

Repository files navigation

FmiSrl.FtpServer

Build, Test and Publish

A lightweight, modern FTP server library for .NET.

Features

  • Fully asynchronous operation.
  • Supports both Dependency Injection and manual instantiation.
  • Extensible authentication and file system providers.
  • Built-in support for common FTP commands (USER, PASS, LIST, RETR, STOR, etc.).
  • PASV mode support.

Installation

dotnet add package FmiSrl.FtpServer.Server

Usage

With Dependency Injection (Recommended)

To use the FTP server in a modern .NET application (e.g., ASP.NET Core, Worker Service), register it in your Program.cs:

usingMicrosoft.Extensions.DependencyInjection;usingFmiSrl.FtpServer.Server;usingFmiSrl.FtpServer.Server.DependencyInjection;usingFmiSrl.FtpServer.Server.Services;varbuilder=Host.CreateApplicationBuilder(args);// Configure Options for Providersbuilder.Services.Configure<PhysicalFileSystemProviderOptions>(opt =>{opt.RootDirectory="./my_ftp_root";});builder.Services.Configure<SimpleAuthenticationProviderOptions>(opt =>{opt.Username="admin";opt.Password="secure_password";});// Add FTP Server and configure providers via generic DIbuilder.Services.AddFtpServer(options =>{options.FtpPort=21;options.ServerName="My Custom FTP Server";}).UseFileSystemProvider<PhysicalFileSystemProvider>().UseAuthenticationProvider<SimpleAuthenticationProvider>();varhost=builder.Build();// Get the server instance and start itvarftpServer=host.Services.GetRequiredService<FtpServer>();awaitftpServer.StartAsync();awaithost.RunAsync();

The server is registered as a Singleton by default.

Without Dependency Injection

You can also use the library in simple console applications or legacy projects without a DI container:

usingFmiSrl.FtpServer.Server;usingFmiSrl.FtpServer.Server.Services;usingMicrosoft.Extensions.Logging.Abstractions;usingMicrosoft.Extensions.Options;varserverOptions=Options.Create(newFtpServerConfigurationOptions{FtpPort=2121,ServerName="Standalone FTP Server"});varfsOptions=Options.Create(newPhysicalFileSystemProviderOptions{RootDirectory="./ftp_root"});varauthOptions=Options.Create(newSimpleAuthenticationProviderOptions{Username="user",Password="password"});// Providers are strictly requiredvarftpServer=newFtpServer(newPhysicalFileSystemProvider(fsOptions),newSimpleAuthenticationProvider(authOptions),Enumerable.Empty<IFtpCommandMiddleware>(),Enumerable.Empty<IFtpServerEventHandler>(),serverOptions,NullLogger<FtpServer>.Instance);awaitftpServer.StartAsync();Console.WriteLine("Server started. Press any key to stop.");Console.ReadKey();awaitftpServer.StopAsync();

Configuration

Passive Data Ports

Every PASV command binds one listener from the configured range, and holds that port until the transfer ends, the client disconnects, or the idle timeout expires:

varserverOptions=Options.Create(newFtpServerConfigurationOptions{PasvMinPort=50000,PasvMaxPort=50100,PasvIdleTimeout=TimeSpan.FromMinutes(2)});

Size the range for the peak number of concurrent transfers, with headroom for ports still in TIME_WAIT after a download. Ports are handed out round-robin, so a port that was just released is the last one retried.

PasvIdleTimeout bounds how long a passive listener waits for the client to dial in. Clients that issue PASV and then never connect — aborted transfers, dropped networks — are the usual cause of a drained range; the timeout reclaims their ports. It is disarmed the moment the data connection is accepted, so it never cuts a slow transfer short. Set it to Timeout.InfiniteTimeSpan to wait forever.

Setting PasvMinPort and PasvMaxPort to 0 disables the range entirely and lets the operating system assign an ephemeral port per data connection, which is only appropriate when no firewall or NAT rule constrains the passive ports. When a configured range runs dry the server falls back to an ephemeral port and logs a warning — that port will usually be blocked by a firewall rule scoped to the range, so the warning is worth alerting on.

Logging

The library uses Microsoft.Extensions.Logging. When using DI, it will automatically use the configured logging providers (e.g., Serilog, Console, Debug).

When used manually, you can pass any ILogger<FtpServer> implementation to the constructor.

Authentication Provider

Implement the IAuthenticationProvider interface to customize how users are authenticated:

publicinterfaceIAuthenticationProvider{Task<bool>AuthenticateAsync(stringusername,stringpassword);}

File System Provider

Implement the IFileSystemProvider interface to provide custom storage (e.g., Azure Blob Storage, Database, S3). It provides an FtpAuthenticationContext which includes the currently authenticated username:

usingFmiSrl.FtpServer.Server.Abstractions;publicinterfaceIFileSystemProvider{Task<IEnumerable<FileSystemEntry>>GetEntriesAsync(FtpAuthenticationContextauthContext,stringpath);Task<Stream>OpenReadAsync(FtpAuthenticationContextauthContext,stringpath);Task<Stream>OpenWriteAsync(FtpAuthenticationContextauthContext,stringpath);TaskDeleteFileAsync(FtpAuthenticationContextauthContext,stringpath);TaskCreateDirectoryAsync(FtpAuthenticationContextauthContext,stringpath);TaskDeleteDirectoryAsync(FtpAuthenticationContextauthContext,stringpath);Task<bool>FileExistsAsync(FtpAuthenticationContextauthContext,stringpath);Task<bool>DirectoryExistsAsync(FtpAuthenticationContextauthContext,stringpath);TaskRenameAsync(FtpAuthenticationContextauthContext,stringoldPath,stringnewPath);}

About

A lightweight, modern FTP server library for .NET.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages