异步改造FTP下载方法后触发文件占用异常,求排查原因
Hi Johny, let's break down why you're running into that "Process cannot access file because it's used by another process" error after converting this method to async, and fix it step by step:
The Core Issues in Your Current Code
Manual resource management is error-prone
You're manually callingread.Close()multiple times, but this isn't as reliable as usingusingstatements. Even though you've added close calls, edge cases (like unexpected exceptions) could still leave the file stream open. Plus, wrapping synchronous code inTask.Runcan create subtle thread-related resource contention that didn't exist in the synchronous version.You're faking async with
Task.Runinstead of using real async APIs
The method is markedasyncbut you're just wrapping synchronous logic inTask.Run—this isn't true asynchronous code. FluentFTP provides native async methods, and not using them defeats the purpose of async while introducing unnecessary thread overhead and resource risks.FtpClient isn't being properly disposed
You're manually callingDisconnect(), butFtpClientimplementsIDisposable—failing to dispose it can leave network connections or file handles hanging, contributing to the "file in use" error.
Fixed Async Implementation
Here's the revised code that addresses all these issues, using proper async patterns and resource management:
public async static Task<bool> DownloadFileFromFTP(string PathToFile, string AppName) { // Check for settings file first if (!File.Exists("settings.xml")) { MessageBox.Show("Missing settings.xml file"); return false; } // Use 'using' to auto-dispose the file stream when done using (var readStream = new FileStream("settings.xml", FileMode.Open, FileAccess.Read, FileShare.Read)) { XmlSerializer serializer = new XmlSerializer(typeof(Information)); Information info = (Information)serializer.Deserialize(readStream); // Use 'using' to auto-dispose FtpClient (handles disconnect and cleanup) using (var ftpClient = new FtpClient(info.HDSynologyIP)) { string decryptedPassword = EncryDecryptor.Decrypt(info.FtpPassword); ftpClient.Credentials = new NetworkCredential(info.FtpUsername, decryptedPassword); try { // Use FluentFTP's native async methods await ftpClient.ConnectAsync(); bool downloadSuccess = await ftpClient.DownloadFileAsync( Path.Combine(info.Downloads, AppName), PathToFile, FtpLocalExists.Overwrite, FtpVerify.Retry ); return downloadSuccess; } catch (Exception ex) { MessageBox.Show(ex.ToString()); return false; } // No need for manual Disconnect() or Close()—'using' handles this automatically } } }
Key Improvements Explained
usingstatements: These ensure thatFileStreamandFtpClientare automatically disposed (and their resources released) when the code block finishes—even if an exception is thrown. This eliminates the risk of leftover file handles causing the "in use" error.- Native FluentFTP async APIs:
ConnectAsyncandDownloadFileAsyncare designed for true asynchronous operations, avoiding the need forTask.Runand the thread-related resource contention it introduced. - Cleaner code flow: Removed redundant
Close()/Disconnect()calls and simplified the logic to make it easier to track resource usage.
Your existing call to await General_Functions.DownloadFileFromFTP(...) is correct and will work with this revised method.
内容的提问来源于stack exchange,提问作者Johny Corbie

