公司的網站項目在用戶上傳文件時出現異常,查看發現是文件路徑過長造成的。
異常如下:
System.IO.PathTooLongException
The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
這句話也很好理解,就是目錄不能超過248字符,整個路徑不能超過260字符。這是Windows系統的限制,我們可以自己新建一個文件夾試一試。
這是我新建的文件夾,當超過248字符時繼續輸入是無效的。加上文件名剛好就是260個字符,就是說文件名輸入12個字符后便不能在輸入了。
我自己在電腦上測試的時候,是報這個異常 System.IO.DirectoryNotFoundException。微軟的文章《More on new .NET path handling》中也給出了解釋:
Note that in many cases the OS will return DirectoryNotFound for paths that are too long. The primary case is with creating files. We don't have a way to programmatically check whether paths are actually too long so we can't translate DirectoryNotFound into PathTooLong. We're looking at updating the exception text in the future to say it "may also be caused by a path that is too long" where such a thing is possible.
帶着問題搜索一下,很容易就能發現這篇微軟官方的文檔 Naming Files, Paths, and Namespaces ,這篇文章和上面的那篇里面都給出了差不多的解決方案,我先把我測試的結論用紅色字體標注了。
1、加前綴 各種項目類型(Web、控制台、WinForm)都適用
\\?\ 例如:"\\?\D:\very long path"。
如果是UNC,就用 \\?\UNC\ ,例如:"\\?\UNC\server\share"。"server" is the name of the computer and "share" is the name of the shared folder,我懶得翻譯了,哈哈。
加前綴為什么可以呢?大概就是Windows API具有許多功能,這些功能也具有Unicode版本,允許使用擴展長度的路徑,最大總路徑長度為32,767個字符,那加個前綴就能指定成使用擴展長度的路徑了。具體可以去文章里看看。
很多第三方的庫為我們封裝好了新的IO方法,也是利用的前綴來解決:
- Delimon.Win32.IO Library
- NuGet:Microsoft.Experimental.IO 官網 用到的地方把系統的File和Directory替換為LongPathFile和LongPathDirectory即可
- NuGet:ZetaLongPaths GitHub
2、同時配置兩個地方(要求Windows 10, Version 1607 以上)ASP.NET Web應用程序 不成功!(我不知道原因啊,如果有大神成功了留言告訴我一下,謝謝),控制台應用和WinForm 成功:
1)系統配置,配置策略或者修改注冊表。
運行gpedit.msc,進入配置編輯器,然后 計算機配置 > 管理模板 > 系統 > 文件系統
找到啟用Win32長路徑,設置為已啟用就行了,然后最好才重啟下電腦。
這個策略就是幫我們在注冊表里面加了一項。
HKLM\SYSTEM\CurrentControlSet\Control\FileSystem LongPathsEnabled (Type: REG_DWORD) 值為 1
如果選擇已禁用,就是把只設置為 0
不知道你注意到上面那個圖里面的“注意”沒有,意思是說你把它改回未配置是沒用的,並不會刪除注冊表。
2)添加應用程序清單文件(application manifest)。
官方文檔告訴我們把這個加進去就行了
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> <ws2:longPathAware>true</ws2:longPathAware> </windowsSettings> </application>
另一篇文檔里面又是這樣的,命名空間的事兒,都差不多。
VS幫我們創建的manifest會默認加入一些東西,只用把下圖這個地方注釋去掉,修改成上面那樣就行了。
改完之后,去項目的屬性里面看一眼,這個地方是不是選擇正確。
好了,官方的文章到這就結束了。然后WinForm應該是OK的,控制台程序有的人成功,有的人不成功~~~
你就搜索吧,微軟的文章需要我們慢慢探索~~~~~
也許你能找到這一篇文章:.NET 4.6.2 and long paths on Windows 10
這里出現了一個關鍵字.NET 4.6.2 , .NET 4.6.2以下版本還要在App.config里配置
<runtime> <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" /> </runtime>
就像這樣
設置了兩個值為false,因為文章里說了:The defaults for these two values are true if the target framework is 4.6.1 or earlier.
到這里OK,大功告成。
大大的疑惑?
加前綴的方案似乎對系統版本和.NET版本沒什么要求,但是要改代碼啊,那么多地方,對我這個懶人來說這太累了。
公司的項目其實用的是.NET 4.7.2的版本。我最開始用WinForm和控制台測試沒問題,我就新建了一個Web項目測試,發現始終不行,微軟官方也沒有對Web項目做特別說明。
第一個遇到的問題是怎么加manifest,因為你打開Web項目的屬性會發現無法設置manifest,我在stackoverflow上看到也有老外在問,但沒有答案。
后來我發現,通過右鍵項目添加app.manifest是有效的(暫且認為吧,這塊我不是太懂),因為我用VS打開生成的dll看了下,如圖。
然而並沒有什么卵用~我就死馬當活馬醫了,又在Web.config中加入了配置,根據官網描述4.6.2以上是不需要的。
還是不行,我感覺是不是Web不是這樣配置的,畢竟官方的例子是控制台程序,又是一頓搜,找到了這篇 <AppContextSwitchOverrides> element
告訴我要這么配置
好!那我就加上。不過事實證明沒用。
有個外國哥們說,我也試過了,if條件都進不去,因為那個值默認就是false,配置不配置沒差別。
bool legacyPaths; if (AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out legacyPaths) && legacyPaths) { var switchType = Type.GetType("System.AppContextSwitches"); if (switchType != null) { AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false); var legacyField = switchType.GetField("_useLegacyPathHandling", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); legacyField?.SetValue(null, (Int32)0); AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out legacyPaths); Assert.IsFalse(legacyPaths, "Long pathnames are not supported!"); } }
在搜索研究了半天之后,最終我放棄了,還是用第一種解決方法吧。
希望這篇文章可以幫助到同樣問題的兄弟,也希望第二個方法有成功的大神告訴我下。
附加:
2020-07-13
今天用VS發布一個網站的時候報了個錯誤:
The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
解決方案如下:
- Unload your web/webjob project
- Select “Edit [yourprojectfilename].csproj“
- Add the following entry under the first <PropertyGroup> that exists in the xml file:
- <IntermediateOutputPath>..\Temp</IntermediateOutputPath>
- Reload the project
- Clean the project
- Try the deployment/publish again – it should work now.
參考文章:
Naming Files, Paths, and Namespaces
.NET 4.6.2 and long paths on Windows 10
Announcing .NET Framework 4.6.2
More on new .NET path handling
<AppContextSwitchOverrides> element
How to deal with files with a name longer than 259 characters?
Error using long paths in .net 4.7