1

次のコードは99%の確率でうまく機能します。

ただし、これを使用して多数のファイルを含むディレクトリをコピーすると(個々のファイルの一部も大きい)、ハングし(例外はスローされません)、コードがIIS 7.5アプリケーションプールをリサイクルするまで、それ以降のFTP要求はハングします。の下で実行されています。(これは、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(); }
}
4

1 に答える 1

1

ついに問題が見つかりました!

"FtpWebRequest.KeepAlive = false;" 動作しますが、閉じた接続をクリアするのに少し時間がかかります。

だから何が起こっていたのかというと、私の最大接続数に達していたということです。(IISのmaxconnectionsがすでに非常に高く設定されているため、.NET maxもあるようです。)

この行を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;
}
于 2012-09-04T20:58:48.530 に答える