我是靠谱客的博主 清爽毛衣,最近开发中收集的这篇文章主要介绍C#文件同步,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

需求描述:

将文件夹A内的文件夹和文件同步到文件夹B。

其实需求也就那么一句话,没啥还需要解释的了吧。详细点说,需要同步文件/文件夹的“新增,删除,重命名,修改”。

一开始我的想法是先Google,然后在博客园找到这篇文章《C#文件同步工具教程》。这篇文章的核心来自msdn里面FileSystemWatcher 的解释。就是用对象FileSystemWatcher 去监听文件是否被创建,重命名,删除,修改。如果发生了就调用相对应的事件,将被修改,创建,重命名的文件复制到目标目录B当中。这个例子比较简单,很多事情都没考虑到。而且我认为用FileSystemWatcher 去监听所有的文件,太浪费CPU和内存。


我的想法

是采用递归,遍历整个源目录,对比目标目录。

    1. 如果目标目录下没有相对应的文件,将文件复制到目标目录;
    2. 如果文件在两个路径下都存在,但是文件大小和最后写入时间不一致时,将原目录下的文件复制到目标目录下;
    3. 如果文件存在于目标目录下而不存在源目录下,则将目标路径下的文件删除。

实现

知道如何比较之后就可以进行递归遍历文件夹了。这个是这个软件实现的难点之一,其实也没多难,也就是说这个软件根本就没多难。以下是递归函数:

复制代码
 1 /// <summary>
2 /// 递归核心 同步目录
3 /// </summary>
4 /// <param name="src">原路径</param>
5 /// <param name="obj">目标路径</param>
6 static void loop(string src, string obj)
// 递归核心 同步目录
7 {
8
CopyFistly(src, obj);
//先同步文件
9
10
//遍历文件夹,递归调用 11
DirectoryInfo dirSrc = new DirectoryInfo(src); 12
DirectoryInfo[] dirs = dirSrc.GetDirectories(); 13
foreach (DirectoryInfo dir in dirs) 14
{ 15
string str = dir.Name; 16
if (Directory.Exists(obj + "\" + dir.Name) == false) 17
{ 18
str = Directory.CreateDirectory(obj + "\" + dir.Name).ToString(); 19
} 20
//注意这里,这里是递归,而且是下面要注意的地方 21
loop(src + "\" + dir.ToString(), obj + "\" + str);
22
} 23 }
复制代码

测试了一下结果,在9000+个文件,40+个文件夹下,在我这部破机器上面单纯递归遍历(不复制文件)的时候需要的时间是截枝的十倍以上。简直是只乌龟。。。


优化

所以要想办法缩短时间提高效率。既然复制文件上面我们无法操作,那我们只好在递归上面进行优化。上个星期我发了一篇文章叫做《算法——回溯法》。这个时候刚好可以用上这种方法了。因为本身用的就是递归,而且文件夹的结构本身就是一个树的结构,在恰好满足了回溯法的要求。在遍历上面,并不需要在所有的文件夹都遍历一遍。因为有些文件夹并没有发生改变,所有就没有必要遍历下去了。所以就需要在递归调用自己之前先加一个条件,也就是加上约束函数。修改之后,代码如下:

复制代码
 1 /// <summary>
2 /// 递归核心 同步目录
3 /// </summary>
4 /// <param name="src">原路径</param>
5 /// <param name="obj">目标路径</param>
6 static void loop(string src, string obj)
// 递归核心 同步目录
7 {
8
CopyFistly(src, obj);
//先同步文件
9
10
//遍历文件夹,递归调用 11
DirectoryInfo dirSrc = new DirectoryInfo(src); 12
DirectoryInfo[] dirs = dirSrc.GetDirectories(); 13
foreach (DirectoryInfo dir in dirs) 14
{ 15
string str = dir.Name; 16
if (Directory.Exists(obj + "\" + dir.Name) == false) 17
{ 18
str = Directory.CreateDirectory(obj + "\" + dir.Name).ToString(); 19
} 20
DirectoryInfo dirObj = new DirectoryInfo(str); 21
//约束函数 在大小不一致的时候进行同步,其他状态不同步 22
if (GetDirectoryLength(src + "\" + dir.ToString()) != GetDirectoryLength(obj + "\" + str))
23
loop(src + "\" + dir.ToString(), obj + "\" + str); 24
} 25 }
复制代码

函数GetDirectoryLength(string path)的作用是检查文件夹path的大小。这里只是简单地对比两个文件夹的大小,如果大小一致,则截枝不递归,否则递归。这种方式的效率非常高,因为很多时候并不是都在有文件的复制,所以不需要经常去遍历目录。所以截枝就好了。下面给出GetDirectoryLength(string path)函数的代码。其实该函数也是一个递归,虽然会增加负荷,但是文件多,文件夹深的时候,是很有必要的。

获取文件夹大小
复制代码
 1 /// <summary>
2 /// 获取路径下文件夹的大小
3 /// </summary>
4 /// <param name="dirPath">目标路径</param>
5 /// <returns>文件夹大小</returns>
6 public static long GetDirectoryLength(string dirPath)
7 {
8
//判断给定的路径是否存在,如果不存在则退出
9
if (!Directory.Exists(dirPath)) 10
return 0; 11
long len = 0; 12
13
//定义一个DirectoryInfo对象 14
DirectoryInfo di = new DirectoryInfo(dirPath); 15
16
//通过GetFiles方法,获取di目录中的所有文件的大小 17
foreach (FileInfo fi in di.GetFiles()) 18
{ 19
len += fi.Length; 20
} 21
22
//获取di中所有的文件夹,并存到一个新的对象数组中,以进行递归 23
DirectoryInfo[] dis = di.GetDirectories(); 24
if (dis.Length > 0) 25
{ 26
for (int i = 0; i < dis.Length; i++) 27
{ 28
len += GetDirectoryLength(dis[i].FullName); 29
} 30
} 31
return len; 32 }
复制代码

难点主要在递归和截枝的思想上面。其他方面的解释可以直接查看代码。注释已经很清楚了。下面是整个文件的源代码:

文件同步
复制代码
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.IO;
6 using System.Threading;
7 using System.Configuration;
8
9 namespace FileSynLoop
10 {
11
class Program
12
{
13
static private string strSource = GetAppConfig("src");
//原路径
14
static private string strObjective = GetAppConfig("obj");
//目标路径
15
static private int synTime = 0;
//同步时间
16
static private string flag = ""; //多线程控制标志 同步控制
17
static private Thread threadShow;
//显示效果线程
18
static private bool bound;
//是否使用截枝函数
19
20
static void Main(string[] args)
21
{
22
//基本设置
23
Console.WriteLine("原路径:" + strSource);
24
Console.WriteLine("目标路径:" + strObjective);
25
try { synTime = Convert.ToInt32(GetAppConfig("synTime")); }
26
catch (Exception e) { Console.WriteLine("配置的同步时间格式不正确:" + e.Message); return; }
27
Console.WriteLine("同步间隔时间:" + synTime + "毫秒");
28
29
if (Directory.Exists(strSource) == false){Console.WriteLine("配置的原路径不存在");return;}
30
if (Directory.Exists(strObjective) == false){Console.WriteLine("配置的目标路径不存在"); return;}
31
32
Console.WriteLine("是否使用截枝函数?使用截止函数无法同步空文件夹/空文件。默认使用截枝!y/n");
33
if (Console.ReadLine() == "n")
34
bound = false;
35
else
36
bound = true;
37
38
do{Console.WriteLine("输入ok开始!");}
39
while (Console.ReadLine() != "ok");
40
41
//线程
42
Thread thread = new Thread(new ThreadStart(ThreadProc));
43
threadShow = new Thread(new ThreadStart(ThreadShow));
44
thread.Start();
45
threadShow.Start(); //开始线程
46
threadShow.Suspend();
//挂起线程
47
//退出
48
while ((flag = Console.ReadLine()) != "exit") ;
49
}
50
51
//线程控制
52
public static void ThreadProc()
53
{
54
int i = 0;
55
DateTime dt;
56
TimeSpan ts;
57
58
while (flag != "exit")
59
{
60
dt = DateTime.Now;
61
Console.WriteLine();
62
Console.Write("第" + ++i + "次同步开始:");
63
threadShow.Resume();
//恢复线程
64
try
65
{
66
loop(strSource, strObjective);
67
}
68
catch (Exception e)
69
{
70
Console.WriteLine("文件夹“" + strSource + "“被占用,暂时无法同步!8:" + e.Message);
71
}
72
threadShow.Suspend();
//挂起线程
73
74
ts = DateTime.Now - dt;
75
Console.WriteLine("|");
76
if (GetDirectoryLength(strSource) == GetDirectoryLength(strObjective))
77
Console.WriteLine("所有同步完毕!");
78
Console.WriteLine("第" + i + "次同步结束,耗时"+ts.ToString()+",正在等待下次开始!");
79
Thread.Sleep(synTime);//同步时间
80
}
81
}
82
83
//显示效果的线程
84
public static void ThreadShow()
85
{
86
while (flag != "exit")
87
{
88
Console.Write(">");
89
Thread.Sleep(500);
90
}
91
}
92
93
/// <summary>
94
/// 递归核心 同步目录
95
/// </summary>
96
/// <param name="src">原路径</param>
97
/// <param name="obj">目标路径</param>
98
static void loop(string src, string obj)
// 递归核心 同步目录
99
{ 100
CopyFistly(src, obj);
//先同步文件 101
102
//遍历文件夹,递归调用 103
DirectoryInfo dirSrc = new DirectoryInfo(src); 104
DirectoryInfo[] dirs = dirSrc.GetDirectories(); 105
foreach (DirectoryInfo dir in dirs) 106
{ 107
string str = dir.Name; 108
if (Directory.Exists(obj + "\" + dir.Name) == false) 109
{ 110
str = Directory.CreateDirectory(obj + "\" + dir.Name).ToString(); 111
} 112
DirectoryInfo dirObj = new DirectoryInfo(str); 113
if (bound) 114
{ 115
//约束函数 在大小不一致的时候进行同步,其他状态不同步 116
if (GetDirectoryLength(src + "\" + dir.ToString()) != GetDirectoryLength(obj + "\" + str))
117
loop(src + "\" + dir.ToString(), obj + "\" + str); 118
} 119
else 120
{ 121
loop(src + "\" + dir.ToString(), obj + "\" + str); 122
} 123
} 124
} 125
126
/// <summary> 127
/// 同步文件 128
/// </summary> 129
/// <param name="strSource">源目录</param> 130
/// <param name="strObjective">目标目录</param> 131
static private void CopyFistly(string strSource, string strObjective)
//同步文件 132
{ 133
string[] srcFileNames = Directory.GetFiles(strSource).Select(s => System.IO.Path.GetFileName(s)).ToArray(); //原路径下的所有文件 134
string[] objFileNames = Directory.GetFiles(strObjective).Select(s => System.IO.Path.GetFileName(s)).ToArray();
//目标路径下的所有文件 135
136
#region 同步新建 修改 137
foreach (string strSrc in srcFileNames) //遍历源文件夹 138
{ 139
FileInfo aFile = new FileInfo(strSource + "\" + strSrc); 140
string aAccessTime = aFile.LastWriteTime.ToString(); 141
string aCreateTime = aFile.CreationTime.ToString(); 142
143
144
string bCreateTime = "";
//目标路径文件的信息 145
string bAccessTime = ""; 146
147
bool flag = false; 148
foreach (string strObj in objFileNames) //遍历目标文件夹 149
{ 150
FileInfo bFile = new FileInfo(strObjective + "\" + strObj); 151
bAccessTime = bFile.LastWriteTime.ToString(); 152
bCreateTime = bFile.CreationTime.ToString(); 153
154
if (strSrc == strObj)
//文件存在目标路径当中 155
{ 156
if (aCreateTime != bCreateTime || aAccessTime != bAccessTime)
//文件存在但是不一致 157
{ 158
try 159
{ 160
File.Copy(strSource + "\" + strSrc, strObjective + "\" + strSrc, true); 161
FileInfo file = new FileInfo(strObjective + "\" + strSrc); 162
file.CreationTime = Convert.ToDateTime(aCreateTime); 163
file.LastAccessTime = Convert.ToDateTime(aAccessTime); 164
} 165
catch (Exception e) 166
{ 167
Console.WriteLine("文件“" + strSrc + "“被占用,暂时无法同步!4:" + e.Message); 168
} 169
} 170
flag = true; 171
break; 172
} 173
} 174
175
if (flag == false)
//文件不存在目标路径当中 176
{ 177
try 178
{ 179
File.Copy(strSource + "\" + strSrc, strObjective + "\" + strSrc, true); 180
FileInfo file = new FileInfo(strObjective + "\" + strSrc); 181
file.CreationTime = Convert.ToDateTime(aCreateTime); 182
file.LastAccessTime = Convert.ToDateTime(aAccessTime); 183
} 184
catch (Exception e) 185
{ 186
Console.WriteLine("文件“" + strSrc + "“被占用,暂时无法同步!5" + e.Message); 187
} 188
} 189
} 190
#endregion 191
192
#region 同步删除 重命名 193
//删除文件 194
foreach (string strObj in objFileNames) //遍历目标文件夹 195
{ 196
string allObj = strObjective + "\" + strObj; 197
bool flag = false; 198
foreach (string strSrc in srcFileNames) //遍历源文件夹 199
{ 200
if (strObj == strSrc) 201
flag = true; 202
} 203
if (flag == false) 204
{ 205
try 206
{ 207
File.Delete(allObj); 208
} 209
catch (Exception e) 210
{ 211
Console.WriteLine("文件“" + strObj + "“被占用,暂时无法同步!6" + e.Message); 212
} 213
} 214
} 215
216
//删除文件夹 217
DirectoryInfo dirSrc = new DirectoryInfo(strSource); 218
DirectoryInfo[] dir***c = dirSrc.GetDirectories(); 219
DirectoryInfo dirObj = new DirectoryInfo(strObjective); 220
DirectoryInfo[] dirsObj = dirObj.GetDirectories(); 221
foreach (DirectoryInfo bdirObj in dirsObj) 222
{ 223
bool flag = false; 224
foreach (DirectoryInfo adirSrc in dir***c) 225
{ 226
if (bdirObj.Name == adirSrc.Name) 227
{ 228
flag = true; 229
} 230
} 231
if (flag == false)
//如果文件夹只出现在目的路径下而不再源目录下,删除该文件夹 232
{ 233
try 234
{ 235
Directory.Delete(dirObj + "\" + bdirObj, true); 236
} 237
catch (Exception e) 238
{ 239
Console.WriteLine("文件夹“" + bdirObj + "“被占用,暂时无法同步!8" + e.Message); 240
} 241
} 242
} 243
#endregion 244
245
} 246
247
/// <summary> 248
/// 获取自定义配置的值 249
/// </summary> 250
/// <param name="strKey">键值</param> 251
/// <returns>键值对应的值</returns> 252
private static string GetAppConfig(string strKey) 253
{ 254
foreach (string key in ConfigurationManager.AppSettings) 255
{ 256
if (key == strKey) 257
{ 258
return ConfigurationManager.AppSettings[strKey]; 259
} 260
} 261
return null; 262
} 263
264
/// <summary>
265
/// 获取路径下文件夹的大小 266
/// </summary> 267
/// <param name="dirPath">目标路径</param> 268
/// <returns>文件夹大小</returns> 269
public static long GetDirectoryLength(string dirPath) 270
{ 271
//判断给定的路径是否存在,如果不存在则退出 272
if (!Directory.Exists(dirPath)) 273
return 0; 274
long len = 0; 275
276
//定义一个DirectoryInfo对象 277
DirectoryInfo di = new DirectoryInfo(dirPath); 278
279
//通过GetFiles方法,获取di目录中的所有文件的大小 280
foreach (FileInfo fi in di.GetFiles()) 281
{ 282
len += fi.Length; 283
} 284
285
//获取di中所有的文件夹,并存到一个新的对象数组中,以进行递归 286
DirectoryInfo[] dis = di.GetDirectories(); 287
if (dis.Length > 0) 288
{ 289
for (int i = 0; i < dis.Length; i++) 290
{ 291
len += GetDirectoryLength(dis[i].FullName); 292
} 293
} 294
return len; 295
} 296
} 297 }
复制代码


效果:

转载于:https://blog.51cto.com/feelmanc/1250539

最后

以上就是清爽毛衣为你收集整理的C#文件同步的全部内容,希望文章能够帮你解决C#文件同步所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部