.NET 现在支持跨平台这件事情已经是众所周知的特点了,虽然平台整体支持跨平台了,但是我们的代码如果真的想要实现跨平台运行其实还是有些小细节要注意的,今天想要记录分享的就是关于 文件I/O操作时路径的拼接问题。

在 Windows 环境下我们常见的路径格式如下:

D:\Software\AppData\Files\aaa.jpg

可以看到 Windows 环境下文分隔符为 \ 路径由三部分组成分别是:

  1. 盘符: D:\
  2. 文件夹层级:Software\AppData\Files
  3. 文件名:aaa.jpg

在 .NET 平台常见的获取当成程序主机路径的方法主要从

.NET 控制台程序,通过依赖注入获取 IHostEnvironment hostEnvironment

.NET Web程序,通过依赖注入获取 IWebHostEnvironment webHostEnvironment

应用程序内容文件的目录的绝对路径

hostEnvironment.ContentRootPath

webHostEnvironment.ContentRootPath

ContentRootPath 指的是应用程序内容文件的目录的绝对路径;


Web 服务应用程序内容文件的目录的绝对路径

webHostEnvironment.WebRootPath

WebRootPath 指的是 其实就是用于存放静态资源的那个 wwwroot 目录的绝对路径,ASP.NET Core MVC 项目的 css、 js、 img 等静态资源一般都是存放在 wwwroot 目录中,ASP.NET Core WebAPI 项目有需要也可以开启这个 wwwroot 的选项,只要在项目启动的时候 app.UseStaticFiles(); 启用静态文件模块即可。


在刚开始接触 .NET 项目时,我代码中的文件上传路径是这样拼接的。

webHostEnvironment.ContentRootPath + "files\\"+ DateTime.UtcNow.ToString("yyyy\\MM\\dd\\")+"xxx.jpg";

这样组合出来的路径地址可能如下:

d:\appdata\files\2022\11\24\xxx.jpg

如果代码这样写,我们在 Windows 平台运行是不会有有任何问题的,但是如果有一天想要尝试跨平台部署,把代码搬到 Linux 或者 Mac 平台运行就会发现这个代码会报错,原因在于 Linux 和 Mac 平台无法识别 \ 分割凭借的文件路径,因为这两个平台是采用 / 做为文件路径分割符的。

比如 Linux 下的常见路径格式如下:

/var/appdata/xxxx

所以这个时候我们只要调整我们的代码为

webHostEnvironment.ContentRootPath + "files/"+ DateTime.UtcNow.ToString("yyyy/MM/dd/")+"xxx.jpg";

凭借出来的路径格式则为

d:/appdata/files/2022/11/24/xxx.jpg



/var/appdata/files/2022/11/24/xxx.jpg

重新编译之后就可以在 Linux 和 Mac 平台运行了,并且 Windows 平台其实也是可以兼容 / 作为文件路径分割符号的,至此三个平台都可以正常运行了。

上面的代码运行了3年左右时间,直至最近更新了 .NET 7 发现上面的代码,在服务器上又报错了,上面的代码执行效果变成了下面这样

d:/appdatafiles/2022/11/24/xxx.jpg



/var/appdatafiles/2022/11/24/xxx.jpg

通过观察可以发现原来是 appdata/files 之间的 分隔符 / 消失了,导致拼接的结果变成了 appdatafiles ,经过调试之后发现原因如下:

在 .NET 6.0 及以前的版本中

webHostEnvironment.ContentRootPath;

webHostEnvironment.WebRootPath;

hostEnvironment.ContentRootPath;

三个变量的末尾都是带有一个分隔符的,他们的取值都是

d:/appdata/var/appdata/ 像这样尾部有跟随一个 / 分割符,但是到了 .NET 7.0 中,他们的取值变了,变成了

d:/appdatavar/appdata 尾部的分割符号不见了,这就导致我们上面的路径拼接代码出现了异常。

这时候想起来微软官方自带的拼接方法 Path.Combine ,该方法用于将多个路径信息进行拼接,改造后的代码如下

Path.Combine(webHostEnvironment.ContentRootPath, "files", DateTime.UtcNow.ToString("yyyy"),DateTime.UtcNow.ToString("MM"),DateTime.UtcNow.ToString("dd"),"xxx.jpg");

这样的到结果如下

d:\appdata\files\2022\11\24\xxx.jpg



/var/appdata/files/2022/11/24/xxx.jpg

可以看到在 Windows 平台运行时还是采用了默认的 \ 作为文件夹的分割符号,而在 Linux 和 Mac 平台运行时则采用了 / 作为文件夹的分割符号。

虽然通过 Path.Combine 可以自动生成符合各个平台运行要求的路径,倒是如果需要把文件路径保存起来的时候还是建议采用 / 作为文件分隔符,这样方便随时切换运行平台,否则 代码在 Windows 平台运行期间产生的数据保存到数据库之后,将来有一天切换到其他平台时这样的路径被查询出来执行时还是会报错,但是采用 / 作为文件分隔符则不需要担心,所以像文件上传方法这种场景在需要记录文件路径到数据库时可以 .Replace("\","/") 对路径进行一下转换之后再保存到数据库中

Path.Combine(webHostEnvironment.ContentRootPath, "files", DateTime.UtcNow.ToString("yyyy"),DateTime.UtcNow.ToString("MM"),DateTime.UtcNow.ToString("dd"),"xxx.jpg").Replace("\\","/");


可能有人会问,为什么 Windows 就不能和 Mac 与 Linux 等系统一样本身也默认采用 / 作为文件分隔符,直接大统一多好,其实这属于历史遗留问题了,因为在 Windows 平台还是 DOC 的时候,那个时候 / 在 Windows 平台是作为命令的参数标记使用的,所以为了不和 命令参数符号 / 重复,就采用最为接近的 \ 充当了路径分隔符,而 Linux 与 Mac 平台传递参数则是采用 - 符号,如我们熟知的 ipconfig 命令。

默认查询的简单信息,如果需要查询全部信息则是

ipconfig /all

如果需要清理 dns 缓存信息则是

ipconfig /flushdns

可以看到传递参数时是需要 / 符号的,当然现在新版的 Windows 系统其实也支持 - 作为参数传递符号了,下面的命令也可以正常运行

ipconfig -all

ipconfig -flushdns

至此 关于 .NET 在不同操作系统中 IO 文件路径拼接方法总结 就讲解完了,有任何不明白的,可以在文章下面评论或者私信我,欢迎大家积极的讨论交流,有兴趣的朋友可以关注我目前在维护的一个 .NET 基础框架项目,项目地址如下

https://github.com/berkerdong/NetEngine.git

https://gitee.com/berkerdong/NetEngine.git

关于 .NET 在不同操作系统中 IO 文件路径拼接方法结升级 .NET 7 后注意到的一个小坑的更多相关文章

  1. Android中获取文件路径的方法总结及对照

    最近在写文件存贮,Android中获取文件路径的方法比较多,所以自己也很混乱.找了好几篇博客,发现了以下的路径归纳,记录一下,以备不时之需 Environment.getDataDirectory() ...

  2. IOS中获取文件路径的方法

    iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. documents,tmp,app,Library. (NSHomeDirectory ...

  3. 【转】c#.net各种应用程序中获取文件路径的方法

    控制台应用程序:Environment.CurrentDirectory.Directory.GetCurrentDirectory() windows服务:Environment.CurrentDi ...

  4. Linux操作系统中打开文件数量的查看方法

    Linux操作系统中打开文件数量的查看方法ulimit -n 4096也就是限制用户的最大文件打开数为4096个 在网上查了关于怎么查看文件打开数的文章大致有两种说法/proc/sys/fs/file ...

  5. 【转】 Linux内核中读写文件数据的方法--不错

    原文网址:http://blog.csdn.net/tommy_wxie/article/details/8193954 Linux内核中读写文件数据的方法  有时候需要在Linuxkernel--大 ...

  6. Java中获取文件路径

    Java中获取文件路径 1.实例说明 (1)得到 ClassPath的绝对URI路径 Thread.currentThread().getContextClassLoader().getResourc ...

  7. js/jquery 获取本地文件的文件路劲 获取input框中type=‘file’ 中的文件路径(转载)

     原文:http://blog.csdn.net/niyingxunzong/article/details/16989947 js/jquery 获取本地文件的文件路劲 获取input框中type= ...

  8. 在Python中使用glob模块查找文件路径的方法

    在Python中使用glob模块查找文件路径的方法 glob模块是最简单的模块之一,内容非常少.用它可以查找符合特定规则的文件路径名.跟使用windows下的文件搜索差不多.查找文件只用到三个匹配符: ...

  9. 在Python中操作文件之truncate()方法的使用教程

    在Python中操作文件之truncate()方法的使用教程 这篇文章主要介绍了在Python中操作文件之truncate()方法的使用教程,是Python入门学习中的基础知识,需要的朋友可以参考下 ...

  10. pip freeze > requirements.txt` 命令输出文件中出现文件路径而非版本号

    pip freeze > requirements.txt 命令输出文件中出现文件路径而非版本号 解决办法: pip list --format=freeze > requirements ...

随机推荐

  1. MySQL建表语句生成Golang代码

    1. 背景 对于后台开发新的需求时,一般会先进行各种表的设计,写各个表的建表语句 然后根据建立的表,写对应的model代码.基础的增删改查代码(基础的增删改查服务可以划入DAO(Data Access ...

  2. SpringBoot 配置文件使用详解

    一.创建一个SpringBoot项目 创建 SprintBoot 项目的 2 种方式: 在 https://start.spring.io/ 上创建一个 SpringBoot 项目,然后导入到 IDE ...

  3. 【译】CLR类型加载器设计

    前言 本文翻译自BotR中的一篇,原文链接 Type Loader Design ,可以帮助我们了解CLR的类型加载机制(注意是Type类型,而不是Class类),文中涉及到术语或者容易混淆的地方,我 ...

  4. 【疑难杂症】关于用pydotplus生成iris.pdf报错问题

    在使用刘建平老师博客中DecisionTreeClassifier实例时,遇到报错:InvocationException: GraphViz's executables not found 源代码如 ...

  5. Readsh中文版初始设置

    B站视频教程网址:https://space.bilibili.com/630285695/video 安装成功后,打开浏览器输入http://ip:5000如果出现如下画面,即告安装成功. 初始设置 ...

  6. 复现CVE-2022-10270(向日葵远程代码执行漏洞)

    警告 请勿使用本文提到的内容违反法律.本文不提供任何担保. 漏洞描述 向日葵是一款免费的,集远程控制电脑手机.远程桌面连接.远程开机.远程管理.支持内网穿透的一体化远程控制管理工具软件.CNVD披露了 ...

  7. java基础二、类与继承

    员工类 Employee, 经理类:Manager public class Employee { private String name; private double salary; privat ...

  8. 关于aws的ec2实例导出成ova后在vmware中的网络配置不生效的问题

    在aws上的ec2实例,尤其是使用了aws市场中的ami创建的linux系统,默认情况下,网络配置都是通过dhcp自动获取的, 这周笔者将一台ec2实例(redhat/linux 8.3)导出/转换成 ...

  9. PHP全栈开发(六):PHP与HTML页面交互

    之前我们在HTML表单学习这篇文章里面创建了一个HTML页面下的表单. 这个表单是用户用来输入数据的 具体代码如下 <!DOCTYPE html> <html> <hea ...

  10. 洛谷P4011 【网络流24题】 孤岛营救问题 (BFS+状压)

    一道妙题啊......(不知道为什么这道题的标签是网络流,不需要用网络流啊) 如果没有门和钥匙,连边(边权为1)求最短路就行了. 但是有这两个因素的限制,我们采用分层建图的思想,一共2p层,每层对应持 ...