2012-08-08 84 views
1

以下代码在99%的时间内运行良好。但是,当我用它复制大量文件(某些单个文件也很大)的目录时,它会挂起(不会抛出异常),并且任何其他FTP请求都会挂起,直到我回收IIS 7.5应用程序代码正在运行的池。 (这用于基于Web的文件浏览器中。)FtpWebRequest挂起大量上传

它不会每次都挂在同一个文件上,实际上让我完全复制目录一次成功,但如果我尝试再次执行它,它会在只复制一些文件和子目录。

我的问题是,任何人都可以看到代码显而易见的问题?有没有一个连接对象没有正确关闭或什么?顺便说一下,我试过(在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(); } 
} 
+0

您是否尝试过'ftpDownloadRequest.Abort();'? – 2012-08-08 16:09:19

+0

字母过多,无法继续 – user854301 2012-08-08 16:09:32

+0

如果您使用普通的ftp客户端(如... ftp或FileZilla或Winscp),IIS的行为是否相同? (即有时挂起) – rene 2012-08-08 16:46:56

回答

1

终于发现了问题!

“FtpWebRequest.KeepAlive = false;”确实有效,但需要一点时间才能清除关闭的连接。

所以发生的是我的最大连接数被击中。 (显然还有一个.NET最大,因为我在IIS中的maxconnections已经设置得非常高。)

将此行添加到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; 
}