1. ホーム
  2. c#

[解決済み] ファイルパスが長すぎる例外を解決する最良の方法

2022-02-28 13:46:54

質問

私はSPサイトのすべてのドキュメントライブラリをダウンロードするアプリを作成しましたが、ある時点でこのエラーが発生しました(Googleで探してみましたが、何も見つかりませんでした。)

System.IO.PathTooLongException: 指定されたパス、ファイル名、またはその両方が長すぎる。完全修飾ファイル名は 260 文字未満、ディレクトリ名は 248 文字未満でなければなりません。 at System.IO.Path.NormalizePathFast(String path, Boolean fullCheck) at System.IO.Path.GetFullPathInternal(String path) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream.ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options) at System.IO.File.Create(String path)

文字列の制限に達したため、以下にコードを示します。

#region Downloading Schemes

    private void btnDownload_Click(object sender, EventArgs e)
    {
        TreeNode currentNode = tvWebs.SelectedNode;
        SPObjectData objectData = (SPObjectData)currentNode.Tag;
        try
        {
            CreateLoggingFile();
            using (SPWeb TopLevelWeb = objectData.Web)
            {
                if(TopLevelWeb != null)
                    dwnEachWeb(TopLevelWeb, TopLevelWeb.Title, tbDirectory.Text);
            }
        }
        catch (Exception ex)
        {
            Trace.WriteLine(string.Format("Exception caught when tried to pass TopLevelWeb:{1}, Title = {2}, object data to (dwnEachWeb_method), Exception: {0}", ex.ToString(), objectData.Web, objectData.Title));
        }
        finally
        {
            CloseLoggingFile();
        }
    }

    private void dwnEachWeb(SPWeb TopLevelWeb, string FolderName, string CurrentDirectory)
    {
        if (TopLevelWeb != null)
        {
            if (TopLevelWeb.Webs != null)
            {
                CurrentDirectory = CurrentDirectory + "\\" + TopLevelWeb.Title;
                CreateFolder(CurrentDirectory);
                foreach (SPWeb ChildWeb in TopLevelWeb.Webs)
                {

                    dwnEachWeb(ChildWeb, ChildWeb.Title, CurrentDirectory);
                    ChildWeb.Dispose();
                }
                dwnEachList(TopLevelWeb, CurrentDirectory);
                //dwnEachList(TopLevelWeb, FolderName, CurrentDirectory);
            }
        }
    }

    private void dwnEachList(SPWeb oWeb, string CurrentDirectory)
    {
        foreach (SPList oList in oWeb.Lists)
        {
            if (oList is SPDocumentLibrary && !oList.Hidden)
            {
                dwnEachFile(oList.RootFolder, CurrentDirectory);
            }
        }
    }

    private void dwnEachFile(SPFolder oFolder, string CurrentDirectory)
    {
        if (oFolder.Files.Count != 0)
        {
            CurrentDirectory = CurrentDirectory + "\\" + oFolder.Name;
            CreateFolder(CurrentDirectory);
            foreach (SPFile ofile in oFolder.Files)
            {
                if (CreateDirectoryStructure(CurrentDirectory, ofile.Url))
                {
                    var filepath = System.IO.Path.Combine(CurrentDirectory, ofile.Url);
                    byte[] binFile = ofile.OpenBinary();
                    System.IO.FileStream fstream = System.IO.File.Create(filepath);
                    fstream.Write(binFile, 0, binFile.Length);
                    fstream.Close();
                }
            }
        }
    }

    //creating directory where files will be download        
    private bool CreateDirectoryStructure(string baseFolder, string filepath)
    {
        if (!Directory.Exists(baseFolder)) return false;

        var paths = filepath.Split('/');

        for (var i = 0; i < paths.Length - 1; i++)
        {
            baseFolder = System.IO.Path.Combine(baseFolder, paths[i]);
            Directory.CreateDirectory(baseFolder);
        }
        return true;
    }

    //creating folders
    private bool CreateFolder(string CurrentDirectory)
    {
        if (!Directory.Exists(CurrentDirectory))
        {
            Directory.CreateDirectory(CurrentDirectory);
        }
        return true;
    }

    //shorting string

    #endregion

解決方法は?

エラーの原因が明らかであるため、問題解決に役立つ情報を紹介します。

こちらをご覧ください ファイル、パス、名前空間の命名に関するMSの記事

以下、リンク先からの引用です。

<ブロッククオート

パスの最大長制限 Windows APIでは(以下の段落で説明する一部の例外を除き)、最大長は1.5mです。 は、MAX_PATH であり、これは 260 文字と定義されている。ローカル パスの構成は、ドライブレター、コロン、パスフレーズの順です。 バックスラッシュ、バックスラッシュで区切られた名前の構成要素、そして終端である ヌル文字。例えば、Dドライブの最大パスは "D:↵、Dドライブの最大パスは "↵です。 256文字のパス文字列<NUL>" ここで、"<NUL>"は不可視のパス文字列を表し、256文字のパス文字列を表します。 現在のシステムコードページの終端ヌル文字。(この 文字 < > は、ここでは視覚的にわかりやすくするために使用されており、その一部となることはできません。 は有効なパス文字列です)。

そして、いくつかの回避策(コメントから抜粋)。

様々な問題を解決する方法があります。以下に挙げる解決策の基本的な考え方は常に同じで、パス長を短縮して path-length + name-length < MAX_PATH . してもよい。

  • サブフォルダーの共有
  • コマンドラインからSUBSTでドライブレターを割り当てる。
  • VBでAddConnectionを使用して、ドライブレターをパスに割り当てます。