在不同平台下面,这里特指Linux、Window,一般我们开发机在window下面,而生产环境在linux平台下,而两个平台处理文件路径不同,稍不注意就会掉坑中。
1.1. 上传文件
2.2. 压缩包解压
Windows下路径为:xxx\yyy\zzz
Linux路径下为:xxx/yyy/zzz
上传时获取文件名称后需要保存在指定目录,文件目录路径处理方案
//方案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("/", "\\");
}
通常我们上传包后解压,供读取文件或文件内容
解压一般用的是System.IO.Compression.ZipFile.dll下类ZipFile的ExtractToDirectory
方法,解压
后的文件在window中文件夹和文件是正常的,在linux下面会把文件路径和文件名加起来作为文件名。
解压效果是这样
预期效果应该是这样
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)
{
}
解压流程
第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);
}