.NET Core 文件路径解决方法,统一Linux Window

.NET Core 文件路径解决方法,统一Linux Window

在不同平台下面,这里特指Linux、Window,一般我们开发机在window下面,而生产环境在linux平台下,而两个平台处理文件路径不同,稍不注意就会掉坑中。

1 场景

1.1. 上传文件
2.2. 压缩包解压

2 平台文件路径处理方式

Windows下路径为:xxx\yyy\zzz
Linux路径下为:xxx/yyy/zzz

3 解决方案

上传

上传时获取文件名称后需要保存在指定目录,文件目录路径处理方案

//方案1  运行时自带
var path1 = Path.Combine("xxx", "yyy", "zzz");
//方案2  反斜杠 两个平台都可以用
var path2 = ("xxx/yyy/zzz");
//方案3 根据不同环境生成不同文件路径,GetRuntimeDirectory 自己编写
//判断平台环境,路径可以任意格式"xxx/yyy\\zzz" 
//实际上多个开发协同的时候就是比较混乱,开发环境都没问题,集成的时候报错频繁
var path3 = GetRuntimeDirectory("xxx/yyy/zzz");

GetRuntimeDirectory方法,需要引用System.Runtime.InteropServices

public static string GetRuntimeDirectory(string path)
{
    if (IsLinuxRunTime())
        return GetLinuxDirectory(path);
    if (IsWindowRunTime())
        return GetWindowDirectory(path);
    return path;
}

public static bool IsWindowRunTime()
{
    return System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
}

public static bool IsLinuxRunTime()
{
    return System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
}

public static string GetLinuxDirectory(string path)
{
    string pathTemp = Path.Combine(path);
    return pathTemp.Replace("\\", "/");
}
public static string GetWindowDirectory(string path)
{
    string pathTemp = Path.Combine(path);
    return pathTemp.Replace("/", "\\");
}

解压

通常我们上传包后解压,供读取文件或文件内容

ZipFile解压

解压一般用的是System.IO.Compression.ZipFile.dll下类ZipFileExtractToDirectory方法,解压
后的文件在window中文件夹和文件是正常的,在linux下面会把文件路径和文件名加起来作为文件名。
解压效果是这样
ZipFile解压后文件格式
预期效果应该是这样
预期解压格式
ZipFIle解压原理

ZipFile源码

解压方法

//sourceArchiveFileName 压缩包的文件路径
//destinationDirectoryName 解压路径
public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName)
{
}
//sourceArchiveFileName 压缩包的文件路径
//destinationDirectoryName 解压路径
//overwriteFiles 如果解压路径中存在某个文件是否覆盖
public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, bool overwriteFiles)
{
}
//sourceArchiveFileName 压缩包的文件路径
//destinationDirectoryName 解压路径
//entryNameEncoding 解压后文件名的编码
public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Encoding entryNameEncoding)
{
}
//sourceArchiveFileName 压缩包的文件路径
//destinationDirectoryName 解压路径
//overwriteFiles 如果解压路径中存在某个文件是否覆盖
//entryNameEncoding 解压后文件名的编码
public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Encoding entryNameEncoding, bool overwriteFiles)
{
}

解压流程

zip解压流程

第4步中方法源码

internal static void ExtractRelativeToDirectory(this ZipArchiveEntry source, string destinationDirectoryName, bool overwrite)
{
      if (source == null)
          throw new ArgumentNullException(nameof(source));

      if (destinationDirectoryName == null)
          throw new ArgumentNullException(nameof(destinationDirectoryName));

      // Note that this will give us a good DirectoryInfo even if destinationDirectoryName exists:
      DirectoryInfo di = Directory.CreateDirectory(destinationDirectoryName);
      string destinationDirectoryFullPath = di.FullName;
      
      //这个地方使用的Path.DirectorySeparatorChar 
      //注意  Path.DirectorySeparatorChar: '\' 
      //     Path.AltDirectorySeparatorChar: '/' 
      //所以该方法解压出来的文件,如果是在文件夹下的文件,文件名为 文件夹+文件
      if (!destinationDirectoryFullPath.EndsWith(Path.DirectorySeparatorChar))
          destinationDirectoryFullPath += Path.DirectorySeparatorChar;

      string fileDestinationPath = Path.GetFullPath(Path.Combine(destinationDirectoryFullPath, source.FullName));

      if (!fileDestinationPath.StartsWith(destinationDirectoryFullPath, PathInternal.StringComparison))
          throw new IOException(SR.IO_ExtractingResultsInOutside);

      if (Path.GetFileName(fileDestinationPath).Length == 0)
      {
          // If it is a directory:

          if (source.Length != 0)
              throw new IOException(SR.IO_DirectoryNameWithData);

          Directory.CreateDirectory(fileDestinationPath);
      }
      else
      {
          // If it is a file:
          // Create containing directory:
          Directory.CreateDirectory(Path.GetDirectoryName(fileDestinationPath)!);
          source.ExtractToFile(fileDestinationPath, overwrite: overwrite);
      }
  }

常见分隔符

Path.DirectorySeparatorChar 字段

// The example displays the following output when run on a Windows system: // Path.DirectorySeparatorChar: '\' 
// Path.AltDirectorySeparatorChar: '/' 
// Path.PathSeparator: ';' 
// Path.VolumeSeparatorChar: ':' 
// Path.GetInvalidPathChars: 
// U+007C) U+0000) U+0001) U+0002) U+0003) U+0004) U+0005) U+0006) U+0007) U+0008) 
// U+0009) U+000A) U+000B) U+000C) U+000D) U+000E) U+000F) U+0010) U+0011) U+0012) 
// U+0013) U+0014) U+0015) U+0016) U+0017) U+0018) U+0019) U+001A) U+001B) U+001C) 
// U+001D) U+001E) U+001F) 
// 
// The example displays the following output when run on a Linux system: // Path.DirectorySeparatorChar: '/' 
// Path.AltDirectorySeparatorChar: '/' 
// Path.PathSeparator: ':' 
// Path.VolumeSeparatorChar: '/' 
// Path.GetInvalidPathChars: 
// U+0000

方案

由于上诉方法为 internal,所以截取部分代码替换流程中的某些步骤,并在拼接后使用方法,以后解压时调用此方法,不要使用ZipFile

fileDestinationPath = GetRuntimeDirectory(fileDestinationPath);

代码

public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName) =>
                 ExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, entryNameEncoding: null, overwriteFiles: false);

public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, bool overwriteFiles) =>
           ExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, entryNameEncoding: null, overwriteFiles: overwriteFiles);


   public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles)
   {
       if (sourceArchiveFileName == null)
           throw new ArgumentNullException(nameof(sourceArchiveFileName));

       using (ZipArchive archive = ZipFile.Open(sourceArchiveFileName, ZipArchiveMode.Read, entryNameEncoding))
       {
           archive.ExtractToDirectoryExtension(destinationDirectoryName, overwriteFiles);
       }
   }



   public static void ExtractToDirectoryExtension(this ZipArchive source, string destinationDirectoryName, bool overwriteFiles)
   {
       if (source == null)
           throw new ArgumentNullException(nameof(source));

       if (destinationDirectoryName == null)
           throw new ArgumentNullException(nameof(destinationDirectoryName));

       foreach (ZipArchiveEntry entry in source.Entries)
       {
           entry.ExtractRelativeToDirectoryExtension(destinationDirectoryName, overwriteFiles);
       }
   }


   public static void ExtractRelativeToDirectoryExtension(this ZipArchiveEntry source, string destinationDirectoryName, bool overwrite)
   {
       if (source == null)
           throw new ArgumentNullException(nameof(source));

       if (destinationDirectoryName == null)
           throw new ArgumentNullException(nameof(destinationDirectoryName));

       // Note that this will give us a good DirectoryInfo even if destinationDirectoryName exists:
       DirectoryInfo di = Directory.CreateDirectory(destinationDirectoryName);
       string destinationDirectoryFullPath = di.FullName;
       if (!destinationDirectoryFullPath.EndsWith(Path.DirectorySeparatorChar))
           destinationDirectoryFullPath += Path.DirectorySeparatorChar;    

       string fileDestinationPath = Path.GetFullPath(Path.Combine(destinationDirectoryFullPath, source.FullName));
       //添加的代码
       fileDestinationPath = GetRuntimeDirectory(fileDestinationPath);

       if (!fileDestinationPath.StartsWith(destinationDirectoryFullPath))
           throw new IOException();

       if (Path.GetFileName(fileDestinationPath).Length == 0)
       {
           // If it is a directory:

           if (source.Length != 0)
               throw new IOException();

           Directory.CreateDirectory(fileDestinationPath);
       }
       else
       {
           // If it is a file:
           // Create containing directory:
           Directory.CreateDirectory(Path.GetDirectoryName(fileDestinationPath)!);
           source.ExtractToFileExtension(fileDestinationPath, overwrite: overwrite);
       }
   }

   public static void ExtractToFileExtension(this ZipArchiveEntry source, string destinationFileName, bool overwrite)
   {
       if (source == null)
           throw new ArgumentNullException(nameof(source));

       if (destinationFileName == null)
           throw new ArgumentNullException(nameof(destinationFileName));

       // Rely on FileStream's ctor for further checking destinationFileName parameter
       FileMode fMode = overwrite ? FileMode.Create : FileMode.CreateNew;

       using (Stream fs = new FileStream(destinationFileName, fMode, FileAccess.Write, FileShare.None, bufferSize: 0x1000, useAsync: false))
       {
           using (Stream es = source.Open())
               es.CopyTo(fs);
       }

       File.SetLastWriteTime(destinationFileName, source.LastWriteTime.DateTime);
   }

猜你喜欢

转载自blog.csdn.net/yiquan_yang/article/details/106859093