访问超过260个字符的长路径文件

9

我正在使用Microsoft Scripting Runtime (FSO)来解析文件夹并生成其所有内容的列表,这些文件夹位于网络上,导致路径最终超过了260个字符。我所拥有的最少代码如下:

Private Sub ProcessFolder(ByVal StrFolder As String)
Dim Fl              As File
Dim Fldr            As Folder
Dim RootFldr        As Folder

Set RootFldr = FS.GetFolder(StrFolder)

    For Each Fl In RootFldr.Files
        Debug.Print Fl.Path
    Next

    For Each Fldr In RootFldr.SubFolders
        DoEvents
        ProcessFolder Fldr.Path
    Next

Set RootFldr = nothing    
End sub

在某个级别上,StrFolder的长度变为259,Set RootFldr ...文件夹行可以工作,但是For Each Fl In RootFldr.Files会报错76: Path not found,可能是因为内容导致路径超过260个字符的限制。
在Windows资源管理器中查看时,文件夹中有文件。我使用Excel作为代码的主机,因为我要将结果输出到工作簿中。
为了更清楚地阐明我的问题及其背景,我需要使用FSO(如果存在替代方法,我很乐意展示)来访问它们在网络路径上深度超过260个字符的文件。我需要使用FSO,因为我拥有的工具需要文件夹路径和文件路径、名称、大小和创建时间等信息。

可能重复:链接 - Kerry White
两者相似,我感到很抱歉没有从一开始就提供更多的信息(我以为我已经问得很好了!)我会更新我的进一步要求,目前我正在使用shortpath来解决它。它破坏了路径(使其不可读/不可用),但我认为我可以编写一些代码来修复它。 - Gary Evans
一个解决方法的想法:在文件路径达到大约250个字符时,您可以为其映射一个新的驱动器号,并递归它,在原始500个字符处再映射另一个驱动器。但是,您需要更改代码以实现重构完整路径。理论上,这样可以达到25*250个字符。 - PatricK
@PatricK 这是一个可能的想法,我认为我在以前的项目中尝试过(很久以前)。问题在于限制在结果路径上,即在将映射的驱动器解析为其路径并将其添加到您要访问的路径之后。 - Gary Evans
@Patrick,我想到的解决方案很相似。我想在WshSell中运行PUSHDPOPD,因为命令的DIR非常优化用于搜索目录(比使用FSO快得多)。然而,一旦我看到Hans的答案,这就没有意义了。他的方法要好得多。 - Tim
3个回答

15

将MAXFILE受限的DOS路径名转换为本地操作系统路径名的技术已经得到了很好的记录和文档化。总结如下:

  • 对于使用驱动器号的路径,在路径前缀加上\\?\,例如\\?\C:\foo\bar\baz.txt
  • 对于使用文件共享的路径,在路径前缀加上'\\?\UNC\,例如\\?\UNC\server\share\baz.txt

在Windows 10上测试时,FileSystemObject也能够正常工作。但在旧版本的Windows或服务器的网络重定向器中可能不一定有效。通过使用FAR文件管理器创建具有长名称的子目录进行验证。

Dim path = "\\?\C:\temp\LongNameTest"
ProcessFolder path

制作:

\\?\c:\temp\LongNameTest\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\VeryLongFolderName0123456789012345678901234567890123456789012345678901234567890123456789\Chrysanthemum.jpg

这段文字共有488个字符。需要注意以下几点:

  • 本地路径名必须是完整路径,不能使用相对路径。也就是说,它们必须始终以驱动器号或共享名称开头,并从驱动器/共享的根目录开始。
  • 你会得到本地路径名,如果要显示它,请不要忘记再次去掉前缀。
  • 尚未测试,但应该会失败,因为文件名本身(不包括目录名)的长度仍然有限制,不能超过259个字符。这不应该是问题,因为用户也无法创建这么长的文件名。

你是如何在FSO上测试的?我无法让它工作,我需要获取文件大小、修改日期和创建日期,我正在使用Set FSFldr = FS.GetFolder("\\?\lo3uppesaapp001\pesa_cmc....) - Gary Evans
1
这不是一个有效的路径,请再次检查帖子。最好的猜测是 \\?\UNC\lo3uppesaapp001\pesa_cmc... - Hans Passant
嗨,我知道这不是一个有效的路径,我不想发布一个实时文件夹路径并阻塞消息。我按照答案中所读的去做。 - Gary Evans
1
要使用UNC指定这样的路径,请使用“\?\UNC\”前缀。例如,“\?\UNC\server\share”,其中“server”是计算机的名称,“share”是共享文件夹的名称。因此,您的示例路径将是“\?\UNC\lo3uppesaapp001\pesa_cmc....”。 - Tim

4
这需要一些有创意的编码,但使用ShortPath是答案。
此工具用于创建根文件夹中每个文件和文件夹的列表,文件也显示其大小和创建/修改日期。问题在于,当文件或文件夹的结果路径超过260个字符时,将抛出错误Error 76: Path Not Found,代码将无法捕获该区域的内容。
使用Microsoft Scripting Runtime(FSO)ShortPath将解决此问题,但路径从人类可读变为编码:-
完整路径 \\ServerName00000\Root_Root_contentmanagement\DPT\STANDARDS_GUIDELINES\VENDOR_CERTIFICATION_FILES\PDFX_CERTIFICATION_ALL\2006_2007\DPT\CompantName0\Approved\Quark\India under Colonial Rule_structure sample\058231738X\Douglas M. Peers_01_058231738X\SUPPORT\ADDITIONAL INFORMATION\IUC-XTG & XML file 短路径 \\lo3uppesaapp001\pesa_cmcoe_contentmanagement\CTS\S4SJ05~5\V275SE~8\PDM5D9~G\2N52EQ~5\HPE\GS9C6L~U\Approved\Quark\IQPSJ5~F\0CWHH1~G\DOFNHA~8\SUPPORT\A6NO7S~K\IUC-XTG & XML file (注意,我已更改完整路径以保护IP和公司信息,但大小相同)
您可以看到,虽然我可以传递短路径给某人,并将其放入Windows Explorer中以到达目的地,但他们只通过查看无法知道它是什么。为了解决这个问题,我使用了一个全局变量,将文件夹路径保留为完整字符串,并遵循短路径所做的操作。然后,这个字符串就是我向用户输出的内容。以下代码简化了操作过程。
简而言之,使用FSO中的ShortPath将解决此问题,但路径可能不美观。
Dim FS              As New FileSystemObject
Dim LngRow          As Long
Dim StrFolderPath   As String
Dim WkBk            As Excel.Workbook
Dim WkSht           As Excel.Worksheet

Public Sub Run_Master()

Set WkBk = Application.Workbooks.Add

    WkBk.SaveAs ThisWorkbook.Path & "\Data.xlsx"

    Set WkSht = WkBk.Worksheets(1)

        WkSht.Range("A1") = "Path"
        WkSht.Range("B1") = "File Name"
        WkSht.Range("C1") = "Size (KB)"
        WkSht.Range("D1") = "Created"
        WkSht.Range("E1") = "Modified"

        LngRow = 2

        Run "\\ServerName00000\AREA_DEPT0_TASK000"

    Set WkSht = Nothing

    WkBk.Close 1
Set WkBk = Nothing

MsgBox "Done!"

End Sub

Private Sub Run(ByVal StrVolumeToCheck As String)
Dim Fldr            As Folder
Dim Fldr2           As Folder

Set Fldr = FS.GetFolder(StrVolumeToCheck)

    'This is the variable that follows the full path name
    StrFolderPath = Fldr.Path

    WkSht.Range("A" & LngRow) = StrFolderPath
    LngRow = LngRow +1

    For Each Fldr2 In Fldr.SubFolders
        If (Left(Fldr2.Name, 1) <> ".") And (UCase(Trim(Fldr2.Name)) <> "LOST+FOUND") Then
            ProcessFolder Fldr2.Path
        End If
    Next

Set Fldr = Nothing

End Sub

Private Sub ProcessFolder(ByVal StrFolder As String)
'This is the one that will will be called recursively to list all files and folders
Dim Fls             As Files
Dim Fl              As File
Dim Fldrs           As Folders
Dim Fldr            As Folder
Dim RootFldr        As Folder

Set RootFldr = FS.GetFolder(StrFolder)

    If (RootFldr.Name <> "lost+found") And (Left(RootFldr.Name, 1) <> ".") Then

        'Add to my full folder path
        StrFolderPath = StrFolderPath & "\" & RootFldr.Name

        WkSht.Range("A" & LngRow) = StrFolderPath
        WkSht.Range("D1") = RootFldr.DateCreated
        WkSht.Range("E1") = RootFldr.DateLastModified
        Lngrow = LngRow + 1

        'This uses the short path to get the files in FSO
        Set Fls = FS.GetFolder(RootFldr.ShortPath).Files

            For Each Fl In Fls
                'This output our string variable of the path (i.e. not the short path)
                WkSht.Range("A" & LngRow) = StrFolderPath
                WkSht.Range("B" & LngRow) = Fl.Name
                WkSht.Range("C" & LngRow) = Fl.Size /1024 '(bytes to kilobytes)
                WkSht.Range("D" & LngRow) = Fl.DateCreated
                WkSht.Range("E" & LngRow) = Fl.DateLastModified

                LngRow = LngRow + 1

            Next
        Set Fls = Nothing

        'This uses the short path to get the sub-folders in FSO
        Set Fldrs = FS.GetFolder(RootFldr.ShortPath).SubFolders
            For Each Fldr In Fldrs
                'Recurse this Proc
                ProcessFolder Fldr.Path
                DoEvents
            Next
        Set Fldrs = Nothing

        'Now we have processed this folder, trim the folder name off of the string
        StrFolderPath = Left(StrFolderPath, Len(StrFolderPath) - Len(RootFldr.Name)+1)

    End If
Set RootFldr = Nothing

End Sub

如上所述,这是一个适用于我示范的剪辑版本的代码,以说明用于解决此限制的方法。实际上,一旦完成,似乎相当基础。


1
哇,运行得很好,谢谢!只有一件事,如果fl.name以“=”字符开头(例如文件名为= .md),可能会失败。在WkSht.Range("B" & LngRow) = Fl.Name之前,我添加了一行代码WkSht.Range("B" & LngRow).NumberFormat = "@"(将单元格格式设置为“文本”而不是“常规”)。 - Sandra Rossi

3

我曾经使用命令行的 subst 命令来解决这个问题。 它可以将本地路径映射为一个驱动器号(有点像网络共享)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接