ftpWebRequest 在大量上传时挂起
本文关键字:挂起 ftpWebRequest | 更新日期: 2024-11-05 21:51:22
下面的代码在99%的时间内运行良好。
但是,当我使用它来复制包含大量文件的目录(某些单个文件也很大)时,它会挂起(没有引发异常),并且任何进一步的 FTP 请求都会挂起,直到我回收运行代码的 IIS 7.5 应用程序池。(这在基于 Web 的文件浏览器中使用。
它不会每次都挂在同一个文件上,实际上让我成功完全复制目录一次,但是如果我尝试再次这样做,它只会在复制一些文件和子目录后挂起。
我的问题是,任何人都可以看到代码的明显问题吗?是否有未正确关闭的连接对象或其他内容?
顺便说一下,我已经尝试(在 FtpCopyFile 方法中)刷新和关闭"uploadStream"对象,以及实例化 FtpWebResponse 对象并随后关闭它。这些变化都没有产生任何影响。
如果代码没有什么明显的,任何人都可以推荐一种跟踪问题的方法吗?由于没有抛出异常,而且我在服务器日志中找不到任何内容(至少我知道要查看的日志),所以我不知所措。
任何帮助将不胜感激!
饲料
public string FtpCopy(string fromUrl, string toUrl, bool isDirectory)
{
string copyResult = "";
// COPY ENTIRE DIRECTORY
if (isDirectory) {
// MAKE SURE TOP DIRECTORY IS CREATED
if (!FtpDirectoryExists(toUrl)) { copyResult += FtpMakeDirectory(toUrl); }
// ITERATE TROUGH ALL FILES AND FOLDERS AND COPY TO LIVE LOCATION
Dictionary<string,Dictionary<string,string>> newItems = FtpRecursiveFileList(fromUrl);
foreach (KeyValuePair<string,Dictionary<string,string>> item in newItems) {
string currentFromUrl = item.Key;
string currentToUrl = currentFromUrl.Replace(fromUrl, toUrl);
if(item.Value["isdirectory"] == "true") { copyResult += FtpMakeDirectory(currentToUrl); }
else { copyResult += FtpCopyFile(currentFromUrl, currentToUrl); }
}
// COPY SINGLE FILE
} else { copyResult = FtpCopyFile(fromUrl, toUrl); }
string returnString = "";
if (copyResult == "") { returnString = "Success"; }
else { returnString = "Error: " + copyResult; }
return returnString;
}
private string FtpMakeDirectory(string url) {
string returnString = "";
// PARSE URL
url = url.TrimEnd('/') + "/";
string[] urlPath = Jbu.Util.UrlToStringArray(url, FTP_PATH_PREFIX);
string currentPath = FTP_PATH_PREFIX + urlPath[0];
// LOOP THROUGH EACH DIRECTORY LEVEL OF PATH
for (int i = 1; i < (urlPath.Length - 1); i++) {
currentPath = currentPath + "/" + urlPath[i];
string[] currentFiles = FtpListDirectoryArray(currentPath);
bool found = false;
if (currentFiles != null) {
// LOOK IN CURRENT DIRECTORY FOR DIRECTORY THAT HAS SAME NAME AS NEXT LOOP'S DIRECTORY
for (int j = 0; j < currentFiles.Length; j++) {
if (currentFiles[j] == urlPath[i + 1]) { found = true; }
}
}
// IF NAME NOT FOUND, CREATE DIRECTORY
if(!found) { returnString += FtpResponseAsString(CreateFtpRequest(currentPath + "/" + urlPath[i + 1], "makedirectory")); }
}
return returnString;
}
private string FtpCopyFile(string fromUrl, string toUrl)
{
string returnString = "";
try {
// GET FILE TO BE COPIED
FtpWebRequest ftpDownloadRequest = CreateFtpRequest(fromUrl, "downloadfile");
System.Net.FtpWebResponse downloadResponse = (System.Net.FtpWebResponse)ftpDownloadRequest.GetResponse();
Stream ftpDownloadStream = downloadResponse.GetResponseStream();
byte[] fileByteArray = Jbu.Util.StreamToByteArray(ftpDownloadStream);
ftpDownloadStream.Close();
// CREATE DIRECTORY, IF NEEDED
string containingDirectory = toUrl.Substring(0,toUrl.LastIndexOf('/'));
if (!FtpDirectoryExists(containingDirectory)) { returnString += FtpMakeDirectory(containingDirectory); }
// UPLOAD FILE TO NEW LOCATION
FtpWebRequest ftpUploadRequest = CreateFtpRequest(toUrl, "uploadfile");
ftpUploadRequest.ContentLength = fileByteArray.Length;
using (Stream uploadStream = ftpUploadRequest.GetRequestStream()) { uploadStream.Write(fileByteArray, 0, fileByteArray.Length); }
} catch (Exception ex) { returnString += "Error: " + ex.ToString(); }
return returnString;
}
private FtpWebRequest CreateFtpRequest(string url, string method)
{
// CREATE REQUEST OBJECT
ServicePointManager.ServerCertificateValidationCallback = (Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) => (certificate.Subject.Contains("CN=" + Jbu.Constant.FTP_CERT_DOMAIN));
FtpWebRequest ftpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
ftpRequest.EnableSsl = true;
ftpRequest.Credentials = new NetworkCredential(Jbu.Constant.FTP_USER, Jbu.Constant.FTP_PASSWORD);
ftpRequest.UseBinary = true;
ftpRequest.KeepAlive = false;
// SET METHOD
switch(method) {
case "listdirectory": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory; break;
case "listdirectorydetails": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; break;
case "makedirectory": ftpRequest.Method = WebRequestMethods.Ftp.MakeDirectory; break;
case "removedirectory": ftpRequest.Method = WebRequestMethods.Ftp.RemoveDirectory; break;
case "downloadfile": ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; break;
case "uploadfile": ftpRequest.Method = WebRequestMethods.Ftp.UploadFile; break;
case "deletefile": ftpRequest.Method = WebRequestMethods.Ftp.DeleteFile; break;
case "getdatetimestamp": ftpRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp; break;
default: break;
}
return ftpRequest;
}
private bool FtpDirectoryExists(string url)
{
bool dirExists = true;
try {
FtpWebRequest ftpRequest = CreateFtpRequest(url + "/", "listdirectory");
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
} catch { dirExists = false; }
return dirExists;
}
private Dictionary<string,Dictionary<string,string>> FtpRecursiveFileList(string url)
{
Dictionary<string,Dictionary<string,string>> returnList = new Dictionary<string,Dictionary<string,string>>();
List<string> files = new List<string>();
Queue<string> folders = new Queue<string>();
folders.Enqueue(url);
while (folders.Count > 0) {
string fld = folders.Dequeue();
Dictionary<string,Dictionary<string,string>> newItems = FtpListDirectoryDetailsArray(fld);
foreach(KeyValuePair<string,Dictionary<string,string>> item in newItems) {
returnList.Add(fld + "/" + item.Key, item.Value);
if(item.Value["isdirectory"] == "true") {
folders.Enqueue(fld + "/" + item.Key);
}
}
}
return returnList;
}
private string[] FtpListDirectoryArray(string ftpPath)
{
FtpWebRequest ftpRequest = CreateFtpRequest(ftpPath, "listdirectory");
List<string> items = new List<string>();
try {
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
Stream responseStream = ftpResponse.GetResponseStream();
using (StreamReader responseReader = new StreamReader(responseStream)) {
string line;
while ((line = responseReader.ReadLine()) != null) { items.Add(line); }
}
} catch { return null; }
string[] itemData = new string[items.Count];
for (int i = 0; i < items.Count; i++) { itemData[i] = items[i]; }
return itemData;
}
private Dictionary<string,Dictionary<string,string>> FtpListDirectoryDetailsArray(string ftpPath)
{
Dictionary<string,Dictionary<string,string>> items = new Dictionary<string,Dictionary<string,string>>();
FtpWebRequest ftpRequest = CreateFtpRequest(ftpPath, "listdirectorydetails");
try {
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
Stream responseStream = ftpResponse.GetResponseStream();
using (StreamReader responseReader = new StreamReader(responseStream)) {
string line;
while ((line = responseReader.ReadLine()) != null) {
Dictionary<string,string> item = new Dictionary<string,string>();
line = System.Text.RegularExpressions.Regex.Replace(line, @"'s+", " "); // REMOVE EXTRA SPACES
string[] itemDetails = line.Split(' ');
item.Add("datetime", itemDetails[0] + " " + itemDetails[1]);
// FOLDERS
if (itemDetails[2] == "<DIR>") {
item.Add("isdirectory", "true");
item.Add("size", "-1");
item.Add("name", itemDetails[3]);
} else {
item.Add("isdirectory", "false");
item.Add("size", itemDetails[2]);
item.Add("name", itemDetails[3]);
}
items.Add(itemDetails[3], item);
}
}
// IF DIRECTORY DOES NOT EXIST, RETURN EMPTY DICT
} catch {};
return items;
}
private string FtpResponseAsString(FtpWebRequest ftpRequest)
{
try {
FtpWebResponse ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
Stream responseStream = ftpResponse.GetResponseStream();
StreamReader responseReader = new StreamReader(responseStream);
return responseReader.ReadToEnd();
} catch (Exception ex) { return "Error: " + ftpRequest.RequestUri + "'n'n" + ex.ToString(); }
}
终于找到了问题所在!
"FtpWebRequest.KeepAlive = false;"确实有效,但清除关闭的连接需要一点时间。
所以发生的事情是我的最大连接数被击中了。(显然还有一个 .NET max,因为我在 IIS 中的最大连接数已经设置得很高。
将此行添加到 CreateFtpConnection 方法中可以解决此问题,因为 IIS 有足够的时间来关闭旧连接并为更多连接腾出空间:ftpRequest.ServicePoint.ConnectionLimit = 1000;
您的特定里程很可能会根据您的文件大小而有所不同,但希望这会对某人有所帮助。
作为记录,以下是CreateFtpConnection现在的样子:
private FtpWebRequest CreateFtpRequest(string url, string method)
{
// CREATE REQUEST OBJECT
ServicePointManager.ServerCertificateValidationCallback = (Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) => (certificate.Subject.Contains("CN=" + Jbu.Constant.FTP_CERT_DOMAIN));
FtpWebRequest ftpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
ftpRequest.EnableSsl = true;
ftpRequest.Credentials = new NetworkCredential(Jbu.Constant.FTP_USER, Jbu.Constant.FTP_PASSWORD);
ftpRequest.UseBinary = true;
ftpRequest.KeepAlive = false;
ftpRequest.ServicePoint.ConnectionLimit = 1000;
// SET METHOD
switch(method) {
case "listdirectory": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory; break;
case "listdirectorydetails": ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; break;
case "makedirectory": ftpRequest.Method = WebRequestMethods.Ftp.MakeDirectory; break;
case "removedirectory": ftpRequest.Method = WebRequestMethods.Ftp.RemoveDirectory; break;
case "downloadfile": ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; break;
case "uploadfile": ftpRequest.Method = WebRequestMethods.Ftp.UploadFile; break;
case "deletefile": ftpRequest.Method = WebRequestMethods.Ftp.DeleteFile; break;
case "getdatetimestamp": ftpRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp; break;
default: break;
}
return ftpRequest;
}