WPF桌面端开发15-文件、文件夹及选择文件

前言

官方文档:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/file-system/

官方文档写的真心不错。

常用路径

系统特殊文件夹的目录路径

https://docs.microsoft.com/zh-cn/dotnet/api/system.environment.specialfolder?redirectedfrom=MSDN&view=netcore-3.1

系统路径

我的文档

1
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

桌面

1
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

数据目录

1
string ApplicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

程序所在文件夹

exe文件所在的目录(不包含xxx.exe)

方法1:

1
2
3
//获取和设置当前目录(即该进程从中启动的目录)的完全限定路径。
// result: X:\xxx\xxx (.exe文件所在的目录)
string str = System.Environment.CurrentDirectory;

方法2:

1
2
3
//获取当前 Thread 的当前应用程序域的基目录,它由程序集冲突解决程序用来探测程序集。
// result: X:\xxx\xxx\ (.exe文件所在的目录+"\")
string str = System.AppDomain.CurrentDomain.BaseDirectory;

方法3:

1
2
3
//获取和设置包含该应用程序的目录的名称。
// result: X:\xxx\xxx\ (.exe文件所在的目录+"\")
string str = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;

方法4:

1
2
3
//获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称。
// result: X:\xxx\xxx (.exe文件所在的目录)
string str = System.Windows.Forms.Application.StartupPath;

方法5:

1
2
//获取应用程序的当前工作目录(不可靠)。
string str = System.IO.Directory.GetCurrentDirectory();

EXE路径

X:\xxx\xxx\xxx.exe (.exe文件所在的目录+.exe文件名)

方式1

1
2
string starupPath = GetType().Assembly.Location;
Console.WriteLine("starupPath:" + starupPath);

方式2

1
2
string starupPath2 = Process.GetCurrentProcess().MainModule.FileName;
Console.WriteLine("starupPath2:" + starupPath2);

方式3

获取启动了应用程序的可执行文件的路径,包括可执行文件的名称。

1
string str = System.Windows.Forms.Application.ExecutablePath;

Download路径

Windows没有为下载文件夹定义CSIDL.aspx),并且通过Environment.SpecialFolder枚举无法使用它。

但是,新的Vista 知名文件夹.aspx) API确实使用ID定义它FOLDERID_Downloads.aspx)。获取实际值的最简单方法可能是SHGetKnownFolderPath.aspx)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class KnownFolder
{
public static readonly Guid Downloads = new Guid("374DE290-123F-4565-9164-39C4925E467B");
}

[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out string pszPath);

private static string GetDownloadPath()
{
string downloadsPath;
SHGetKnownFolderPath(KnownFolder.Downloads, 0, IntPtr.Zero, out downloadsPath);
Console.WriteLine(downloadsPath);
return downloadsPath;
}

路径拼接

1
Path.Combine(ZConfig.basePath, "images")

注意

这样路径拼接后类似于E:\project\schoolclient\bin\x86\Debug\images,没有最后的反斜杠

参数可以多个,类型必须为字符串

另外一定要注意

参与拼接的路径不要以\或者/开始,这样返回的路径不是拼接的路径。

比如

1
Path.Combine("D:/test/", "/images/1.png");

我们获取到的结果是/images/1.png,而不是D:/test/images/1.png

这样才是正确的

1
Path.Combine("D:/test/", "images/1.png");

分隔符

在 Windows 上,反斜杠 \ 被广泛地用作路径分隔符,例如 C:\Users\username\Documents

Path相关方法返回的路径的分隔符也是\

在 Linux 和 macOS 等基于 Unix 的操作系统上,正斜杠 / 被用作路径分隔符,例如 /home/username/Documents

C#中有两个分隔符

  • Path.DirectorySeparatorChar 对应的是 \
  • Path.AltDirectorySeparatorChar对应的是 /

这两个分隔符在Windows上都是支持的,即使一个路径中两种分隔符都存在也没有问题。

但是在Linux上只支持/,为了兼容我们可以把路径分割符进行替换

1
filepath = filepath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

这样可以确保路径格式在不同操作系统上都是正确的。

如果程序只运行在Windows上,则不用替换分隔符。

需要注意的是,在使用 Path.DirectorySeparatorCharPath.AltDirectorySeparatorChar 时,应该尽量避免手动拼接路径字符串,而是使用 Path.Combine() 方法来完成路径拼接,以保证路径在不同操作系统下都能正确处理。

文件夹

创建文件夹

创建文件夹的方法要注意以下两点:

  • 创建文件夹时候是能直接创建多层的,不用一层一层创建。
  • 创建文件夹可以不判断之前存在不存在,直接调用不会报错也不会删除之前文件夹下的文件。

方式1

1
2
3
4
5
6
var dirPath = ZConfig.cacheImagePath;
DirectoryInfo directoryInfo = new DirectoryInfo(dirPath);
if (!directoryInfo.Exists)
{
System.IO.Directory.CreateDirectory(dirPath);
}

方式2

1
2
3
4
5
6
string savePath =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "XHCLASS");
if (!Directory.Exists(savePath))
{
System.IO.Directory.CreateDirectory(savePath);
}

获取文件所在文件夹

获取父文件夹

1
2
3
string filePath = @"C:\path\to\file.txt";
string folderPath = Path.GetDirectoryName(filePath);
Console.WriteLine("文件所在文件夹的路径: " + folderPath);

创建父文件夹

1
2
3
4
5
6
FileInfo fi = new FileInfo(filepath);
var di = fi.Directory;
if (!di.Exists)
{
di.Create();
}

删除文件夹

1
2
3
4
5
DirectoryInfo tongpingPath = new DirectoryInfo(ZConfig.classTongpingPath());
if (tongpingPath.Exists)
{
tongpingPath.Delete(true);
}

也可以

1
2
3
4
if (Directory.Exists(tempPath))
{
Directory.Delete(tempPath, true);
}

其中

Delete(true)会自动删除子文件夹。

删除文件夹前一定要判断文件夹是否存在,否则会异常。

获取文件夹下文件

只获取下一层的文件

方式1

1
2
3
4
5
string[] files = Directory.GetFiles(path, "*.txt");
foreach (string file in files)
{
Console.WriteLine(file);
}

方式2

1
2
3
4
5
DirectoryInfo folder = new DirectoryInfo(path);
foreach (FileInfo file in folder.GetFiles("*.txt"))
{
Console.WriteLine(file.FullName);
}

所有目录和文件名

递归地输出当前运行程序所在的磁盘下的所有文件名和子目录名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
static void Main(string[] args)
{
//获取当前程序所在的文件路径
String rootPath = Directory.GetCurrentDirectory();
string parentPath = Directory.GetParent(rootPath).FullName;//上级目录
string topPath = Directory.GetParent(parentPath).FullName;//上上级目录
StreamWriter sw = null;
try
{
//创建输出流,将得到文件名子目录名保存到txt中
sw = new StreamWriter(new FileStream("fileList.txt", FileMode.Append));
sw.WriteLine("根目录:" + topPath);
getDirectory(sw, topPath, 2);
}
catch (IOException e)
{
Console.WriteLine(e.Message);
}
finally
{
if (sw != null)
{
sw.Close();
Console.WriteLine("完成");
}
}

}

/// <summary>
/// 获得指定路径下所有文件名
/// </summary>
/// <param name="sw">文件写入流</param>
/// <param name="path">文件写入流</param>
/// <param name="indent">输出时的缩进量</param>
public static void getFileName(StreamWriter sw, string path, int indent)
{
DirectoryInfo root = new DirectoryInfo(path);
foreach (FileInfo f in root.GetFiles())
{
for (int i = 0; i < indent; i++)
{
sw.Write(" ");
}
sw.WriteLine(f.Name);
}
}

/// <summary>
/// 获得指定路径下所有子目录名
/// </summary>
/// <param name="sw">文件写入流</param>
/// <param name="path">文件夹路径</param>
/// <param name="indent">输出时的缩进量</param>
public static void getDirectory(StreamWriter sw, string path, int indent)
{
getFileName(sw, path, indent);
DirectoryInfo root = new DirectoryInfo(path);
foreach (DirectoryInfo d in root.GetDirectories())
{
for (int i = 0; i < indent; i++)
{
sw.Write(" ");
}
sw.WriteLine("文件夹:" + d.Name);
getDirectory(sw, d.FullName, indent + 2);
sw.WriteLine();
}
}

获取文件名

c#根据绝对路径获取 带后缀文件名、后缀名、文件名。

1
2
3
4
string str =" E:\test\Default.aspx";
string filename = System.IO.Path.GetFileName(str);//文件名 “Default.aspx”
string extension = System.IO.Path.GetExtension(str);//扩展名 “.aspx”
string fileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(str);// 没有扩展名的文件名 “Default”

2、c#根据绝对路径获取 带后缀文件名、后缀名、文件名,使用 Split 函数。

1
2
3
4
string str = =" E:\test\Default.aspx";
char[] delimiterChars = { '.', '\\' };
string[] Mystr = str.Split(delimiterChars);
string sheetName = Mystr[Mystr.Length - 2];);// 没有扩展名的文件名 “Default”

3、C# 获取文件名及扩展名

1
2
string aFirstName = aFile.Substring(aFile.LastIndexOf("\\") + 1, (aFile.LastIndexOf(".") - aFile.LastIndexOf("\\") - 1));  //文件名
string aLastName = aFile.Substring(aFile.LastIndexOf(".") + 1, (aFile.Length - aFile.LastIndexOf(".") - 1)); //扩展名

还有的就是用Substring截取

1
2
strFilePaht.Substring(path.LastIndexOf("\\") + 1, path.Length - 1 - path.LastIndexOf("\\"));
strFilePaht.Substring(path.LastIndexOf("."), path.Length - path.LastIndexOf("."));

或者用openFileDialog1.SafeFileName

这样就能取到该文件的所在目录路径

1
string path1 = System.IO.Path.GetDirectoryName(openFileDialog1.FileName) + @"\";

文件

判断文件是否占用

方式1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// <summary>
/// 文件是否占用
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static bool IsFileInUse(string fileName)
{
bool inUse = true;

FileStream fs = null;
try
{
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
inUse = false;
}
catch
{
// ignored
}
finally
{
if (fs != null) { fs.Close(); }
}
return inUse; //true表示正在使用,false没有使用
}

方式2(推荐)

推荐使用这种方式 控制台不会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[DllImport("kernel32.dll")]
public static extern IntPtr _lopen(string lpPathName, int iReadWrite);

[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);

/// <summary>
/// 文件是否占用
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static bool IsFileInUse(string fileName)

{
const int ofReadwrite = 2;
const int ofShareDenyNone = 0x40;
IntPtr hfileError = new IntPtr(-1);
if (!File.Exists(fileName))
{
return false;
}

IntPtr vHandle = _lopen(fileName, ofReadwrite | ofShareDenyNone);

if (vHandle == hfileError)
{
return true;
}

CloseHandle(vHandle);
return false;
}

文件删除

1
2
3
4
5
//删除文件
if (File.Exists(path2))
{
File.Delete(path2);
}

文件复制

1
2
3
4
if (File.Exists(SavePath))
{
File.Copy(TempVideoPathName, SavePath);
}

文件移动/重命名

1
2
3
4
5
FileInfo fi = new FileInfo(TempVideoPathName);
if (fi.Exists)
{
fi.MoveTo(SavePath);
}

或者

1
2
3
4
if (File.Exists(SavePath))
{
File.Move(TempVideoPathName, SavePath);
}

生成新文件路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.IO;

namespace Z.Common
{
public class ZPathUtil
{
private static int temp = 100;

public static string GetPathJpeg(string basePath)
{
temp += 1;
if (temp > 1000)
{
temp = 101;
}

string filename = DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_ffff") + "_" + temp + ".jpg";
string pathAll = Path.Combine(basePath, filename);
return pathAll;
}
}
}

覆盖写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.IO;
using System.Threading.Tasks;

class WriteAllText
{
public static async Task ExampleAsync()
{
string text =
"A class is the most powerful data type in C#. Like a structure, " +
"a class defines the data and behavior of the data type. ";

await File.WriteAllTextAsync("WriteText.txt", text);
}
}

同步

1
File.WriteAllText(filePath, content);

追加写入

1
2
3
4
5
6
7
8
9
10
11
using System.IO;
using System.Threading.Tasks;

class StreamWriterTwo
{
public static async Task ExampleAsync()
{
using StreamWriter file = new("WriteLines2.txt", append: true);
await file.WriteLineAsync("Fourth line");
}
}

同步

1
File.AppendAllText(filePath, content);

读取文本文件

1
2
3
4
5
6
7
8
9
10
11
12
int counter = 0;  

// Read the file and display it line by line.
foreach (string line in System.IO.File.ReadLines(@"c:\test.txt"))
{
System.Console.WriteLine(line);
counter++;
}

System.Console.WriteLine("There were {0} lines.", counter);
// Suspend the screen.
System.Console.ReadLine();

异常

以下情况可能会导致异常:

使用文件系统时,还有其他可能会导致异常的情况,因此最好进行防御性编程。

获取屏幕二进制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static byte[] CaptureScreen()
{
int iw = Screen.PrimaryScreen.Bounds.Width;
int ih = Screen.PrimaryScreen.Bounds.Height;

if (iw != 0 && ih != 0)
{
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(iw, ih);
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(iw, ih));

graphics.Dispose();
byte[] bt = null;
using (MemoryStream mostream = new MemoryStream())
{
Bitmap bm1 = new Bitmap(bitmap, 1280, 800);
bm1.Save(mostream, System.Drawing.Imaging.ImageFormat.Jpeg);//将图像以指定的格式存入缓存内存流

bt = new byte[mostream.Length];

bitmap.Dispose();
bitmap = null;
bm1.Dispose();
bm1 = null;
return bt;
}
}
return null;
}

保存屏幕截图到文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private static int iw = Screen.PrimaryScreen.Bounds.Width;
private static int ih = Screen.PrimaryScreen.Bounds.Height;

public static string CaptureScreenSave(string filePath)
{
try
{
using (Bitmap mbmp = new Bitmap(iw, ih))
{
using (Graphics g = Graphics.FromImage(mbmp))
{
g.CopyFromScreen(0, 0, 0, 0, new Size(iw, ih));
}

using (Bitmap postImage = new Bitmap(mbmp, 1280, 800))
{
if (postImage != null)
{
postImage.Save(filePath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}

return filePath;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}

注意

这真是个大坑,上面的截屏并保存文件的代码明明该释放的都释放了,但是循环执行的时候内存还是会一直增长,反复实验发现,Bitmap调用Save方法后,内存并不会释放,只要我们在生成图片文件的同时延迟删除生成的图片内存就不会增长了,真是迷了!!!

延迟删除图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void deleteFile(string filepath)
{
new Thread(
o =>
{
Thread.Sleep(30000);
FileInfo fi = new FileInfo(filepath);
if (fi.Exists)
{
fi.Delete();
}
}
).Start();
}

保存图片

获取图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Drawing;
using System.Windows.Forms;

namespace SchoolClient.Utils
{
internal class ZScreenUtil
{
private static int iw = Screen.PrimaryScreen.Bounds.Width;
private static int ih = Screen.PrimaryScreen.Bounds.Height;
private static Bitmap bmp = null;
private static Bitmap postImage = null;

/// <summary>
/// 截取屏幕
/// </summary>
/// <returns></returns>
public static Bitmap CaptureScreen()
{
try
{
if (bmp != null)
{
bmp.Dispose();
}
bmp = new Bitmap(iw, ih);

using (Graphics g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(0, 0, 0, 0, new Size(iw, ih));
}
if (postImage != null)
{
postImage.Dispose();
}
postImage = new Bitmap(bmp, 1280, 800);
return postImage;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}
}
}

保存图片

1
2
3
4
Bitmap img = ZScreenUtil.CaptureScreen();
var filePath = "E:\123.jpg";
img.Save(filePath, System.Drawing.Imaging.ImageFormat.Jpeg);
img.Dispose();

保存图片并压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static string CaptureScreenSave(string filePath)
{
ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/jpeg");
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(Encoder.Quality, 60L);
myEncoderParameters.Param[0] = myEncoderParameter;
img.Save(filePath, myImageCodecInfo, myEncoderParameters);
img.Dispose();
}

private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}

保存二进制到文件

1
2
3
4
5
6
7
using (Stream fls = new FileStream(filePath, FileMode.Create))
{
using (BinaryWriter bw = new BinaryWriter(fls))
{
bw.Write(buffer);
}
}

文件夹/文件复制移动

循环访问目录树

如果你确信拥有指定根目录下的所有目录的访问权限,则可以使用 System.IO.SearchOption.AllDirectories 标志。 此标志返回与指定的模式匹配的所有嵌套的子目录。

1
root.GetDirectories("*.*", System.IO.SearchOption.AllDirectories);

此方法的缺点是,如果指定根目录下的任何子目录引发 DirectoryNotFoundExceptionUnauthorizedAccessException 异常,则整个方法失败且不返回任何目录。 使用 GetFiles 方法时也是如此。 如果需要处理特定子文件夹中的异常,则必须手动遍历目录树,如以下示例所示。

手动遍历目录树时,可以先处理文件(前序遍历),或者先处理子目录(后序遍历)。 如果执行前序遍历,则可直接访问该文件夹本身下的文件,然后遍历当前文件夹下的整个树。 后序遍历是另一种方法,在访问当前文件夹的文件之前遍历下面的整个树。 本文档后面的示例执行的是前序遍历,但你可以轻松地修改它们以执行后序遍历。

获取目录下所有文件

1
root.GetFiles("*.*");

另一种选择是,是使用递归遍历还是基于堆栈的遍历。

本文档后面的示例演示了这两种方法

下面的示例演示如何以递归方式遍历目录树。

递归方法是一种很好的方法,但是如果目录树较大且嵌套深度较深,则可能引起堆栈溢出异常。

在每个文件或文件夹上处理的特定异常和执行的特定操作仅作为示例提供。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class RecursiveFileSearch
{
static System.Collections.Specialized.StringCollection log = new System.Collections.Specialized.StringCollection();

static void Main()
{
// Start with drives if you have to search the entire computer.
string[] drives = System.Environment.GetLogicalDrives();

foreach (string dr in drives)
{
System.IO.DriveInfo di = new System.IO.DriveInfo(dr);

// Here we skip the drive if it is not ready to be read. This
// is not necessarily the appropriate action in all scenarios.
if (!di.IsReady)
{
Console.WriteLine("The drive {0} could not be read", di.Name);
continue;
}
System.IO.DirectoryInfo rootDir = di.RootDirectory;
WalkDirectoryTree(rootDir);
}

// Write out all the files that could not be processed.
Console.WriteLine("Files with restricted access:");
foreach (string s in log)
{
Console.WriteLine(s);
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key");
Console.ReadKey();
}

static void WalkDirectoryTree(System.IO.DirectoryInfo root)
{
System.IO.FileInfo[] files = null;
System.IO.DirectoryInfo[] subDirs = null;

// First, process all the files directly under this folder
try
{
files = root.GetFiles("*.*");
}
catch (UnauthorizedAccessException e)
{
log.Add(e.Message);
}
catch (System.IO.DirectoryNotFoundException e)
{
Console.WriteLine(e.Message);
}

if (files != null)
{
foreach (System.IO.FileInfo fi in files)
{
Console.WriteLine(fi.FullName);
}

// Now find all the subdirectories under this directory.
subDirs = root.GetDirectories();

foreach (System.IO.DirectoryInfo dirInfo in subDirs)
{
// Resursive call for each subdirectory.
WalkDirectoryTree(dirInfo);
}
}
}
}

下面的示例演示如何不使用递归方式遍历目录树中的文件和文件夹。

此方法使用泛型 Stack 集合类型,此集合类型是一个后进先出 (LIFO) 堆栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public class StackBasedIteration
{
static void Main(string[] args)
{
// Specify the starting folder on the command line, or in
// Visual Studio in the Project > Properties > Debug pane.
TraverseTree(args[0]);

Console.WriteLine("Press any key");
Console.ReadKey();
}

public static void TraverseTree(string root)
{
// Data structure to hold names of subfolders to be
// examined for files.
Stack<string> dirs = new Stack<string>(20);

if (!System.IO.Directory.Exists(root))
{
throw new ArgumentException();
}
dirs.Push(root);

while (dirs.Count > 0)
{
string currentDir = dirs.Pop();
string[] subDirs;
try
{
subDirs = System.IO.Directory.GetDirectories(currentDir);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine(e.Message);
continue;
}
catch (System.IO.DirectoryNotFoundException e)
{
Console.WriteLine(e.Message);
continue;
}

string[] files = null;
try
{
files = System.IO.Directory.GetFiles(currentDir);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine(e.Message);
continue;
}
catch (System.IO.DirectoryNotFoundException e)
{
Console.WriteLine(e.Message);
continue;
}
// Perform the required action on each file here.
// Modify this block to perform your required task.
foreach (string file in files)
{
try
{
// Perform whatever action is required in your scenario.
System.IO.FileInfo fi = new System.IO.FileInfo(file);
Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime);
}
catch (System.IO.FileNotFoundException e)
{
// If file was deleted by a separate application
// or thread since the call to TraverseTree()
// then just continue.
Console.WriteLine(e.Message);
continue;
}
}

// Push the subdirectories onto the stack for traversal.
// This could also be done before handing the files.
foreach (string str in subDirs){
dirs.Push(str);
}
}
}
}

通常,检测每个文件夹以确定应用程序是否有权限打开它是一个很费时的过程。 因此,此代码示例只将此部分操作封装在 try/catch 块中。 你可以修改 catch 块,以便在拒绝访问某个文件夹时,可以尝试提升权限,然后再次访问此文件夹。 一般来说,仅捕获可以处理的、不会将应用程序置于未知状态的异常。

如果必须在内存或磁盘上存储目录树的内容,那么最佳选择是仅存储每个文件的 FullName 属性(类型为 string)。 然后可以根据需要使用此字符串创建新的 FileInfoDirectoryInfo 对象,或打开需要进行其他处理的任何文件。

复制文件和目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Simple synchronous file copy operations with no user interface.
// To run this sample, first create the following directories and files:
// C:\Users\Public\TestFolder
// C:\Users\Public\TestFolder\test.txt
// C:\Users\Public\TestFolder\SubDir\test.txt
public class SimpleFileCopy
{
static void Main()
{
string fileName = "test.txt";
string sourcePath = @"C:\Users\Public\TestFolder";
string targetPath = @"C:\Users\Public\TestFolder\SubDir";

// Use Path class to manipulate file and directory paths.
string sourceFile = System.IO.Path.Combine(sourcePath, fileName);
string destFile = System.IO.Path.Combine(targetPath, fileName);

// To copy a folder's contents to a new location:
// Create a new target folder.
// If the directory already exists, this method does not create a new directory.
System.IO.Directory.CreateDirectory(targetPath);

// To copy a file to another location and
// overwrite the destination file if it already exists.
System.IO.File.Copy(sourceFile, destFile, true);

// To copy all the files in one directory to another directory.
if (System.IO.Directory.Exists(sourcePath))
{
string[] files = System.IO.Directory.GetFiles(sourcePath);

// Copy the files and overwrite destination files if they already exist.
foreach (string s in files)
{
// Use static Path methods to extract only the file name from the path.
fileName = System.IO.Path.GetFileName(s);
destFile = System.IO.Path.Combine(targetPath, fileName);
System.IO.File.Copy(s, destFile, true);
}
}
else
{
Console.WriteLine("Source path does not exist!");
}

// Keep console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}

移动文件和目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Simple synchronous file move operations with no user interface.
public class SimpleFileMove
{
static void Main()
{
string sourceFile = @"C:\Users\Public\public\test.txt";
string destinationFile = @"C:\Users\Public\private\test.txt";

// To move a file or folder to a new location:
System.IO.File.Move(sourceFile, destinationFile);

// To move an entire directory. To programmatically modify or combine
// path strings, use the System.IO.Path class.
System.IO.Directory.Move(@"C:\Users\Public\public\test\", @"C:\Users\Public\private");
}
}

删除文件和目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Simple synchronous file deletion operations with no user interface.
// To run this sample, create the following files on your drive:
// C:\Users\Public\DeleteTest\test1.txt
// C:\Users\Public\DeleteTest\test2.txt
// C:\Users\Public\DeleteTest\SubDir\test2.txt

public class SimpleFileDelete
{
static void Main()
{
// Delete a file by using File class static method...
if(System.IO.File.Exists(@"C:\Users\Public\DeleteTest\test.txt"))
{
// Use a try block to catch IOExceptions, to
// handle the case of the file already being
// opened by another process.
try
{
System.IO.File.Delete(@"C:\Users\Public\DeleteTest\test.txt");
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
return;
}
}

// ...or by using FileInfo instance method.
System.IO.FileInfo fi = new System.IO.FileInfo(@"C:\Users\Public\DeleteTest\test2.txt");
try
{
fi.Delete();
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
}

// Delete a directory. Must be writable or empty.
try
{
System.IO.Directory.Delete(@"C:\Users\Public\DeleteTest");
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
}
// Delete a directory and all subdirectories with Directory static method...
if(System.IO.Directory.Exists(@"C:\Users\Public\DeleteTest"))
{
try
{
System.IO.Directory.Delete(@"C:\Users\Public\DeleteTest", true);
}

catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
}
}

// ...or with DirectoryInfo instance method.
System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(@"C:\Users\Public\public");
// Delete this dir and all subdirs.
try
{
di.Delete(true);
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
}
}
}

选择文件

WPF中,调用OpenFileDialog时有两个选择:

  1. System.Windows.Forms.OpenFileDialog
  2. Microsoft.Win32.OpenFileDialog

方式1

1
2
3
4
5
6
7
8
9
10
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Filter = "文档|*.docx;*.doc";
ofd.AutoUpgradeEnabled = false;
ofd.Multiselect = false;
ofd.AddExtension = true;
ofd.DereferenceLinks = true;
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string filepath = ofd.FileName;
}

方式2(推荐)

选择图片

1
2
3
4
5
6
7
8
9
10
Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
ofd.Filter = "文档|*.docx;*.doc|图片|*.jpg;*.jpeg;*.png;*.bmp";
ofd.InitialDirectory = desktopPath;
ofd.Multiselect = false;
ofd.AddExtension = true;
ofd.DereferenceLinks = true;
if(ofd.ShowDialog() == true)
{
Target = ofd.FileName;
}

过滤具体文件

1
2
3
4
5
6
7
8
9
10
11
Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog
{
Filter = "FFMPEG程序|ffmpeg.exe",
Multiselect = false,
AddExtension = true,
DereferenceLinks = true
};
if (ofd.ShowDialog() == true)
{
SaveTb.Text = ofd.FileName;
}

选择文件夹

1
2
3
4
5
6
7
8
9
10
11
System.Windows.Forms.FolderBrowserDialog dialog = new System.Windows.Forms.FolderBrowserDialog();
dialog.Description = "请选择保存的文件夹";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
if (string.IsNullOrEmpty(dialog.SelectedPath))
{
return;
}
folderTB.Text = dialog.SelectedPath;
Console.WriteLine(dialog.SelectedPath);
}