.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. Elasticsearch-shell脚本实现定时删除指定时间以前索引

    〇.前言 因为elastiflow的数据量还是挺大的,接入了两台交换机的flow数据量已经开始有点大了.所以得写个脚本专门来清理索引 一.如何使用elastic的API 1.手动查询所有索引 在ELK ...

  2. 中秋快乐!新鲜出炉一篇DjangoAdmin使用合集,DjangoAdmin的功能比你想象的强大!

    DjangoAdmin DjangoAdmin本身就是一套大而全的系统,官方文档中介绍了很多配置方法,但仍然有大量的骚操作是文档中没有的,所以遇到特殊需求的时候,求助文档不一定有用. 在我看来 Dja ...

  3. 【SQLServer】max worker threads参数配置

    查看和设置max worker threads USE master; //选中你想设置max worker threads的数据库.master表示在实例级别进行设置 GO EXEC sp_conf ...

  4. 《吐血整理》高级系列教程-吃透Fiddler抓包教程(21)-如何使用Fiddler生成Jmeter脚本-上篇

    1.简介 通过跟随宏哥的脚步学习宏哥的Jmeter系列文章,.我们知道Jmeter本身可以录制脚本,也可以通过BadBoy,BlazeMeter等工具进行录制,其实Fiddler也可以录制Jmter脚 ...

  5. null和undefined的区别、数组和伪数组的区别

    null和undefined的区别 undefined: 字面意思是未定义的值,语义是希望表示一个变量最原始的状态,而非人为操作的结果.这种原始状态会在以下四个场景中出现: 声明了一个变量但没有赋值 ...

  6. 从云AK泄露利用看企业特权管理

    从云AK泄露利用看企业特权管理 目录 - 缘起 - 当前主流AK泄露检测方式 - 防止AK滥用的关键要素? - 哪些算特权账号管理? - 如何做特权账号管理? - 特权管理与堡垒机.IAM.零信任的关 ...

  7. G&GH05 删除文件和.gitignore

    注意事项与声明 平台: Windows 10 作者: JamesNULLiu 邮箱: jamesnulliu@outlook.com 博客: https://www.cnblogs.com/james ...

  8. Pod 使用进阶

    静态 Pod 在 Kubernetes 集群中除了我们经常使用到的普通的 Pod 外,还有一种特殊的 Pod,叫做Static Pod,也就是我们说的静态 Pod,静态 Pod 有什么特殊的地方呢? ...

  9. 使用ingress-nginx

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247496824&idx=1&sn=cd845d77 ...

  10. 使用MinIO中暂未解决的问题

    时间显示问题 web页面上创建桶的时间跟使用SDK获取的时间不一样,相差8个小时,但是mc命令行客户端获取的时间跟web上的一样