开发接收FTP上传转HTTP POST的Web应用方案咨询(PHP/C#)
Hey there! Let's solve this problem step by step. The core challenge here is bridging the gap between your client's FTP-only XML uploads and your IIS server's need to receive that data via HTTP POST. Here's how to build a lightweight "FTP-to-HTTP proxy" that handles this seamlessly, with both C# (ideal for your Windows/IIS environment) and PHP options:
We’ll deploy a minimal, purpose-built FTP server that only accepts XML file uploads. As soon as an XML file is received, the server will read its content and forward it to your target endpoint via an HTTP POST request. No full FTP server features (like downloads or directory browsing) are needed, keeping the setup secure and lightweight.
C#/.NET Solution (Recommended for Windows/IIS Environments)
Since you’re running IIS on Windows, a C# solution integrates smoothly with your existing stack. Here’s how to implement it:
Step 1: Use a Lightweight FTP Server Library
Skip building an FTP server from scratch—use the open-source FubarDev.FtpServer library, which is designed for easy customization and event handling. Install it via NuGet:
Install-Package FubarDev.FtpServer Install-Package FubarDev.FtpServer.FileSystem.DotNet
Step 2: Build the FTP-to-HTTP Proxy
Create a .NET Console App (convertible to a Windows Service for background running) with this core logic:
using FubarDev.FtpServer; using FubarDev.FtpServer.FileSystem.DotNet; using System.Net.Http; using System.Text; using System.Threading.Tasks; var host = new HostBuilder() .ConfigureServices(services => { // Configure temp storage for uploaded files services.AddFtpServer(builder => { builder.UseDotNetFileSystem(@"C:\temp\ftp_uploads"); // Replace with your temp path // Add user authentication here if needed (instead of anonymous) builder.AddAnonymousAuthentication(); }); services.AddSingleton<IFtpHost>(provider => provider.GetRequiredService<IFtpServerHost>()); }) // Uncomment below to run as a Windows Service // .UseWindowsService() .Build(); // Hook into file upload events var ftpHost = host.Services.GetRequiredService<IFtpHost>(); ftpHost.UploadedFile += async (sender, args) => { var filePath = args.FileSystemEntry.FullName; if (Path.GetExtension(filePath).Equals(".xml", StringComparison.OrdinalIgnoreCase)) { // Read XML content var xmlContent = await File.ReadAllTextAsync(filePath); // Forward via HTTP POST using var httpClient = new HttpClient(); var postContent = new StringContent(xmlContent, Encoding.UTF8, "application/xml"); var response = await httpClient.PostAsync("https://your-target-endpoint.com/upload", postContent); // Clean up and respond if (response.IsSuccessStatusCode) { File.Delete(filePath); Console.WriteLine($"Successfully forwarded XML: {filePath}"); } else { Console.WriteLine($"Failed to forward XML: {response.StatusCode}"); // Add retry logic or error logging here } } }; await host.RunAsync();
Step 3: Deploy for Long-Term Use
- Convert the console app to a Windows Service using the
Microsoft.Extensions.Hosting.WindowsServicesNuGet package (add.UseWindowsService()to the HostBuilder) to run it in the background without a console window. - Open your firewall for the FTP port you choose (e.g., 2121, since default port 21 may be restricted).
Key Resources & APIs
FubarDev.FtpServer: Docs for configuring authentication, FTPS encryption, and additional event hooks.HttpClient: Built-in .NET class for HTTP requests (add retry logic with thePollylibrary for transient errors).Polly: For robust retry policies to handle temporary HTTP endpoint failures.
PHP Solution (Cross-Platform Option)
If you prefer PHP, you can build an async FTP server using ReactPHP, which handles concurrent uploads efficiently. Here’s how:
Step 1: Set Up Dependencies
Install ReactPHP’s FTP server and HTTP client via Composer:
composer require react/ftp-server guzzlehttp/guzzle
Step 2: Build the Proxy Script
Create a CLI script (ftp-proxy.php) with this logic:
<?php require __DIR__ . '/vendor/autoload.php'; $loop = React\EventLoop\Factory::create(); $tempDir = __DIR__ . '/temp_uploads'; // Create temp directory if it doesn't exist if (!is_dir($tempDir)) mkdir($tempDir); $server = new React\FtpServer\Server(function ($connection) use ($tempDir) { // Handle file uploads (STOR command) $connection->on('STOR', function ($path, $writeStream) use ($tempDir, $connection) { $fileName = basename($path); if (strtolower(pathinfo($fileName, PATHINFO_EXTENSION)) !== 'xml') { $connection->send('550 Only XML files are allowed'); $writeStream->close(); return; } $tempPath = $tempDir . '/' . uniqid() . '.xml'; $fileStream = fopen($tempPath, 'w'); // Pipe uploaded content to temp file $writeStream->pipe($fileStream)->on('close', function () use ($tempPath, $connection) { // Forward XML via HTTP POST $client = new GuzzleHttp\Client(); try { $response = $client->post('https://your-target-endpoint.com/upload', [ 'headers' => ['Content-Type' => 'application/xml'], 'body' => file_get_contents($tempPath) ]); if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) { unlink($tempPath); $connection->send('226 Transfer complete and forwarded successfully'); } else { $connection->send('451 Failed to forward file to target endpoint'); } } catch (Exception $e) { $connection->send('451 Error forwarding file: ' . $e->getMessage()); // Log error details here } }); }); // Disable unused FTP commands for security $connection->on('LIST', fn() => $connection->send('550 Not supported')); $connection->on('RETR', fn() => $connection->send('550 Not supported')); }); // Start server on port 2121 $server->listen(2121, '0.0.0.0', $loop); echo "FTP proxy running on port 2121\n"; $loop->run();
Step 3: Run the Script
- Execute via CLI:
php ftp-proxy.php - For background running on Windows, use the
nssm(Non-Sucking Service Manager) tool to wrap the script as a Windows Service.
Key Resources & APIs
- ReactPHP FTP Server: Async, lightweight FTP implementation for handling concurrent connections.
- Guzzle: Robust HTTP client for PHP with built-in retry middleware options.
Critical Security & Reliability Notes
- Restrict Access: Limit FTP connections to trusted IPs, use FTPS encryption (both libraries support this), and avoid anonymous access unless absolutely necessary.
- Validate XML: Add schema validation to ensure uploaded files match your expected format before forwarding.
- Error Handling: Implement retry logic for HTTP POST failures, log all uploads/forwards, and set up alerts for critical errors.
- Cleanup: Automatically delete old temp files (even failed uploads) to prevent disk space bloat.
内容的提问来源于stack exchange,提问作者plavozont

