├── .gitignore
├── FrmFilterTester.Designer.cs
├── FrmFilterTester.cs
├── FrmFilterTester.resx
├── FrmMain.Designer.cs
├── FrmMain.cs
├── FrmMain.resx
├── LICENSE
├── Program.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── QiNiuDrive.csproj
├── QiNiuDrive.csproj.DotSettings
├── README.md
├── Resources
├── FrmMain
│ ├── qiniu_logo.png
│ ├── tab_button_setting_chosen.png
│ └── unknown.jpg
└── Icons
│ ├── icon.ico
│ └── logo.png
├── VERSION.json
├── app.config
├── imgs
└── SyncSetting.png
└── libs.rar
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Bb]in/
3 | [Oo]bj/
4 |
5 | # mstest test results
6 | TestResults
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Rr]elease/
19 | x64/
20 | *_i.c
21 | *_p.c
22 | *.ilk
23 | *.meta
24 | *.obj
25 | *.pch
26 | *.pdb
27 | *.pgc
28 | *.pgd
29 | *.rsp
30 | *.sbr
31 | *.tlb
32 | *.tli
33 | *.tlh
34 | *.tmp
35 | *.log
36 | *.vspscc
37 | *.vssscc
38 | .builds
39 |
40 | # Visual C++ cache files
41 | ipch/
42 | *.aps
43 | *.ncb
44 | *.opensdf
45 | *.sdf
46 |
47 | # Visual Studio profiler
48 | *.psess
49 | *.vsp
50 | *.vspx
51 |
52 | # Guidance Automation Toolkit
53 | *.gpState
54 |
55 | # ReSharper is a .NET coding add-in
56 | _ReSharper*
57 |
58 | # NCrunch
59 | *.ncrunch*
60 | .*crunch*.local.xml
61 |
62 | # Installshield output folder
63 | [Ee]xpress
64 |
65 | # DocProject is a documentation generator add-in
66 | DocProject/buildhelp/
67 | DocProject/Help/*.HxT
68 | DocProject/Help/*.HxC
69 | DocProject/Help/*.hhc
70 | DocProject/Help/*.hhk
71 | DocProject/Help/*.hhp
72 | DocProject/Help/Html2
73 | DocProject/Help/html
74 |
75 | # Click-Once directory
76 | publish
77 |
78 | # Publish Web Output
79 | *.Publish.xml
80 |
81 | # NuGet Packages Directory
82 | packages
83 |
84 | # Windows Azure Build Output
85 | csx
86 | *.build.csdef
87 |
88 | # Windows Store app package directory
89 | AppPackages/
90 |
91 | # Others
92 | [Bb]in
93 | [Oo]bj
94 | sql
95 | TestResults
96 | [Tt]est[Rr]esult*
97 | *.Cache
98 | ClientBin
99 | [Ss]tyle[Cc]op.*
100 | ~$*
101 | *.dbmdl
102 | Generated_Code #added for RIA/Silverlight projects
103 |
104 | # Backup & report files from converting an old project file to a newer
105 | # Visual Studio version. Backup files are not needed, because we have git ;-)
106 | _UpgradeReport_Files/
107 | Backup*/
108 | UpgradeLog*.XML
109 |
110 | *.sln
111 | libs/
--------------------------------------------------------------------------------
/FrmFilterTester.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace QiNiuDrive
2 | {
3 | partial class FrmFilterTester
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.lsvFiles = new System.Windows.Forms.ListView();
32 | this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
33 | this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
34 | this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
35 | this.SuspendLayout();
36 | //
37 | // lsvFiles
38 | //
39 | this.lsvFiles.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
40 | this.columnHeader1,
41 | this.columnHeader2,
42 | this.columnHeader3});
43 | this.lsvFiles.FullRowSelect = true;
44 | this.lsvFiles.Location = new System.Drawing.Point(6, 76);
45 | this.lsvFiles.Name = "lsvFiles";
46 | this.lsvFiles.ShowItemToolTips = true;
47 | this.lsvFiles.Size = new System.Drawing.Size(550, 345);
48 | this.lsvFiles.TabIndex = 0;
49 | this.lsvFiles.UseCompatibleStateImageBehavior = false;
50 | this.lsvFiles.View = System.Windows.Forms.View.Details;
51 | //
52 | // columnHeader1
53 | //
54 | this.columnHeader1.Text = "文件名";
55 | this.columnHeader1.Width = 378;
56 | //
57 | // columnHeader2
58 | //
59 | this.columnHeader2.Text = "过滤";
60 | this.columnHeader2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
61 | this.columnHeader2.Width = 50;
62 | //
63 | // columnHeader3
64 | //
65 | this.columnHeader3.Text = "应用规则";
66 | this.columnHeader3.Width = 100;
67 | //
68 | // FrmFilterTester
69 | //
70 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
71 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
72 | this.ClientSize = new System.Drawing.Size(563, 471);
73 | this.Controls.Add(this.lsvFiles);
74 | this.Font = new System.Drawing.Font("Microsoft YaHei", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
75 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
76 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
77 | this.Name = "FrmFilterTester";
78 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
79 | this.Text = "FrmFilterTester";
80 | this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.FrmFilterTester_MouseClick);
81 | this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.FrmFilterTester_MouseDown);
82 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.FrmFilterTester_MouseMove);
83 | this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.FrmFilterTester_MouseUp);
84 | this.ResumeLayout(false);
85 |
86 | }
87 |
88 | #endregion
89 |
90 | private System.Windows.Forms.ListView lsvFiles;
91 | private System.Windows.Forms.ColumnHeader columnHeader1;
92 | private System.Windows.Forms.ColumnHeader columnHeader2;
93 | private System.Windows.Forms.ColumnHeader columnHeader3;
94 | }
95 | }
--------------------------------------------------------------------------------
/FrmFilterTester.cs:
--------------------------------------------------------------------------------
1 | #region 文档说明
2 | /* ******************************************************************************************************
3 | * 文档作者:无闻
4 | * 创建日期:2013 年 10 月 20 日
5 | * 文档用途:七牛云盘过滤规则测试窗口
6 | * ------------------------------------------------------------------------------------------------------
7 | * 修改记录:
8 | * ------------------------------------------------------------------------------------------------------
9 | * 参考文献:
10 | * ******************************************************************************************************/
11 | #endregion
12 |
13 | #region 命名空间引用
14 | using System.IO;
15 | using System.Windows.Forms;
16 | using System.Drawing;
17 | using System.Collections.Generic;
18 |
19 | using CharmCommonMethod;
20 | using CharmControlLibrary;
21 | #endregion
22 |
23 | namespace QiNiuDrive
24 | {
25 | // 七牛云盘过滤规则测试窗口
26 | public partial class FrmFilterTester : Form
27 | {
28 | #region 常量
29 | private const int TITLE_HEIGHT = 30; // 标题栏高度
30 | #endregion
31 |
32 | #region 字段
33 | // * 用户控件 *
34 | private List mControls; // 控件集合
35 | // 0-测试目录文本框
36 | private List mCharmControls; // Charm 控件集合
37 | private readonly ToolTip mToolTip = new ToolTip(); // 工具提示文本控件
38 | #endregion
39 |
40 | #region 窗体事件
41 | // 窗体构造方法
42 | public FrmFilterTester()
43 | {
44 | // 初始化组件
45 | InitializeComponent();
46 | // 设置双缓冲模式
47 | this.SetStyle(ControlStyles.AllPaintingInWmPaint | //不擦除背景 ,减少闪烁
48 | ControlStyles.OptimizedDoubleBuffer | //双缓冲
49 | ControlStyles.UserPaint, //使用自定义的重绘事件,减少闪烁
50 | true);
51 | this.UpdateStyles();
52 | // 初始化设置
53 | InitializeSetting();
54 | }
55 |
56 | // 窗体鼠标单击事件
57 | private void FrmFilterTester_MouseClick(object sender, MouseEventArgs e)
58 | {
59 | // 调用事件
60 | CharmControl.MouseClickEvent(e, mCharmControls);
61 | }
62 |
63 | // 窗体鼠标按下事件
64 | private void FrmFilterTester_MouseDown(object sender, MouseEventArgs e)
65 | {
66 | // 调用事件
67 | if (!CharmControl.MouseDownEvent(e, mCharmControls, this))
68 | APIOperation.MoveNoBorderForm(this, e);
69 | }
70 |
71 | // 窗体鼠标移动事件
72 | private void FrmFilterTester_MouseMove(object sender, MouseEventArgs e)
73 | {
74 | // 调用事件
75 | CharmControl.MouseMoveEvent(e, mCharmControls, this, mToolTip);
76 | }
77 |
78 | // 窗体鼠标弹起事件
79 | private void FrmFilterTester_MouseUp(object sender, MouseEventArgs e)
80 | {
81 | // 调用事件
82 | CharmControl.MouseUpEvent(e, mCharmControls, this);
83 | }
84 | #endregion
85 |
86 | #region 重载事件
87 | // 控件重绘事件
88 | protected override void OnPaint(PaintEventArgs e)
89 | {
90 | // 获取绘制对象
91 | Graphics g = e.Graphics;
92 |
93 | // 测试目录
94 | g.DrawString("模拟同步目录:", this.Font, Brushes.Black, 20, TITLE_HEIGHT + 15);
95 |
96 | // 绘制控件
97 | CharmControl.PaintEvent(e.Graphics, mCharmControls);
98 |
99 | // 重置重绘索引
100 | //mMultipleSettingRedrawIndex = 0;
101 | }
102 | #endregion
103 |
104 | #region 控件事件
105 | // 关闭按钮被单击事件
106 | private void btnClose_MouseClick(object sender, MouseEventArgs e)
107 | {
108 | this.Hide();
109 | }
110 |
111 | // 浏览路径按钮被单击事件
112 | private void btnViewPath_MouseClick(object sender, MouseEventArgs e)
113 | {
114 | string testDir = ((CharmTextBox)mControls[0]).Text;
115 | // 创建并实例化文件浏览对话框
116 | FolderBrowserDialog folderBrowserFialog = new FolderBrowserDialog
117 | {
118 | Description = "请选择测试目录",
119 | SelectedPath = testDir
120 | };
121 |
122 | // 显示对话框并判断用户是否指定新的目录
123 | if (folderBrowserFialog.ShowDialog() == DialogResult.OK)
124 | {
125 | testDir = folderBrowserFialog.SelectedPath; // 用户指定新的目录
126 | ((CharmTextBox)mControls[0]).Text = testDir;
127 | IniOperation.WriteValue(FrmMain.APP_CFG_PATH, "cache", "test_filter_dir", testDir);
128 | }
129 | }
130 |
131 | // 测试规则按钮被单击事件
132 | private void btnTest_MouseClick(object sender, MouseEventArgs e)
133 | {
134 | // 初始化
135 | lsvFiles.Items.Clear();
136 | List filterList = new List();
137 |
138 | // 加载过滤器列表
139 | FrmMain.LoadFilterList(filterList);
140 |
141 | // 开始过滤
142 | LoadLocalFiles(filterList, "");
143 | }
144 | #endregion
145 |
146 | #region 方法
147 | // 初始化设置
148 | private void InitializeSetting()
149 | {
150 | // 设置窗体属性
151 | this.Icon = Properties.Resources.icon;
152 | this.Text = "过滤规则测试";
153 |
154 | // 绘制窗体背景
155 | DrawFormBackground();
156 |
157 | #region 创建窗体组件
158 | // 创建关闭系统按钮
159 | CharmSysButton btnClose = new CharmSysButton
160 | {
161 | SysButtonType = SysButtonType.Close,
162 | ToolTipText = "关闭",
163 | Location = new Point(this.Width - 44, 1)
164 | };
165 |
166 | // 创建测试目录文本框
167 | CharmTextBox txtTestDir = new CharmTextBox
168 | {
169 | Location = new Point(110, 10 + TITLE_HEIGHT),
170 | Width = 330
171 | };
172 | // 创建浏览路径按钮
173 | CharmButton btnViewPath = new CharmButton
174 | {
175 | ButtonType = ButtonType.Classic_Size_08223,
176 | Text = "浏览路径",
177 | ForeColor = Color.DarkGreen,
178 | Location = new Point(455, 12 + TITLE_HEIGHT)
179 | };
180 |
181 | // 创建测试规则按钮
182 | CharmButton btnTest = new CharmButton
183 | {
184 | ButtonType = ButtonType.Classic_Size_12425,
185 | Text = "开始测试规则",
186 | ForeColor = Color.MediumSlateBlue,
187 | Location = new Point(420, 435),
188 | };
189 |
190 | // 将控件添加到集合中
191 | this.Controls.Add(txtTestDir);
192 |
193 | // 创建控件集合
194 | mControls = new List { txtTestDir };
195 | mCharmControls = new List { btnClose, btnViewPath, btnTest };
196 |
197 | // 关联控件事件
198 | btnClose.MouseClick += btnClose_MouseClick;
199 | btnViewPath.MouseClick += btnViewPath_MouseClick;
200 | btnTest.MouseClick += btnTest_MouseClick;
201 | #endregion
202 |
203 | // 加载本地设置
204 | LoadLocalSetting();
205 | }
206 |
207 | // 绘制窗体背景
208 | private void DrawFormBackground()
209 | {
210 | // 加载窗体背景图像资源
211 | Bitmap imgBkg = new Bitmap(this.Width, this.Height);
212 |
213 | #region 绘制窗体内容
214 | using (Graphics g = Graphics.FromImage(imgBkg))
215 | {
216 | // 设置参数
217 | const int bottomHeight = 45;
218 |
219 | // 绘制标题栏背景
220 | SolidBrush sb = new SolidBrush(Color.DodgerBlue);
221 | g.FillRectangle(sb, 0, 0, this.Width, TITLE_HEIGHT);
222 | // 绘制窗口图标
223 | g.DrawImage(Properties.Resources.logo, new Rectangle(6, 5, 20, 20));
224 | // 绘制窗口标题
225 | g.DrawString("过滤规则测试 - 七牛云盘",
226 | new Font("微软雅黑", 10, FontStyle.Bold), Brushes.White, new Point(30, 6));
227 | // 绘制主面板区
228 | sb.Color = Color.FromArgb(255, Color.WhiteSmoke);
229 | g.FillRectangle(sb, 1, TITLE_HEIGHT, this.Width - 2, this.Height - TITLE_HEIGHT - bottomHeight);
230 | // 绘制横线
231 | g.DrawLine(Pens.DarkGray, 1, this.Height - bottomHeight, this.Width - 2, this.Height - bottomHeight);
232 | // 绘制按钮区
233 | sb.Color = Color.FromArgb(70, Color.LightGray);
234 | g.FillRectangle(sb, 1, this.Height - bottomHeight, this.Width - 2, bottomHeight);
235 | // 绘制边框
236 | g.DrawRectangle(new Pen(Color.DimGray), 0, 0, this.Width - 1, this.Height - 1);
237 | }
238 | #endregion
239 |
240 | // 设置背景资源
241 | this.BackgroundImage = new Bitmap(imgBkg);
242 |
243 | // 释放系统资源
244 | imgBkg.Dispose();
245 | }
246 |
247 | // 加载本地设置
248 | private void LoadLocalSetting()
249 | {
250 | // 测试目录
251 | string testDir = IniOperation.ReadValue(FrmMain.APP_CFG_PATH, "cache", "test_filter_dir");
252 | if (testDir.Length > 0 && Directory.Exists(testDir))
253 | {
254 | ((CharmTextBox)mControls[0]).Text = testDir;
255 | }
256 | }
257 |
258 | // 加载本地文件(可用于递归操作)
259 | private void LoadLocalFiles(List filterList, string dirPrefix)
260 | {
261 | // 检查文件
262 | DirectoryInfo di = new DirectoryInfo(((CharmTextBox)mControls[0]).Text + "\\" + dirPrefix);
263 | try
264 | {
265 | foreach (FileInfo fi in di.GetFiles())
266 | {
267 | string fileName = (dirPrefix.TrimStart('\\') + "\\" + fi.Name).TrimStart('\\').Replace("\\", "/");
268 |
269 | bool isFilter = false;
270 | foreach (Filter f in filterList)
271 | {
272 | switch (f.Type)
273 | {
274 | case FilterType.Contain:
275 | if (fileName.Contains(f.Name))
276 | {
277 | lsvFiles.Items.Add(new ListViewItem(new[] { fileName, "是", "*" + f.Name + "*" }));
278 | isFilter = true;
279 | }
280 | break;
281 | case FilterType.Prefix:
282 | if (fileName.StartsWith(f.Name))
283 | {
284 | lsvFiles.Items.Add(new ListViewItem(new[] { fileName, "是", f.Name + "*" }));
285 | isFilter = true;
286 | }
287 | break;
288 | case FilterType.Suffix:
289 | if (fileName.EndsWith(f.Name))
290 | {
291 | lsvFiles.Items.Add(new ListViewItem(new[] { fileName, "是", "*" + f.Name }));
292 | isFilter = true;
293 | }
294 | break;
295 | case FilterType.FullMatch:
296 | if (fileName.Equals(f.Name))
297 | {
298 | lsvFiles.Items.Add(new ListViewItem(new[] { fileName, "是", f.Name }));
299 | isFilter = true;
300 | }
301 | break;
302 | }
303 |
304 | if (isFilter)
305 | break;
306 | }
307 |
308 | if (!isFilter)
309 | lsvFiles.Items.Add(new ListViewItem(new[] { fileName, "否", "-" }));
310 | }
311 |
312 | // 检查目录
313 | foreach (DirectoryInfo subDi in di.GetDirectories())
314 | LoadLocalFiles(filterList, dirPrefix + "\\" + subDi.Name);
315 | }
316 | // ReSharper disable once EmptyGeneralCatchClause
317 | catch { }
318 | }
319 | #endregion
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/FrmFilterTester.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/FrmMain.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace QiNiuDrive
2 | {
3 | partial class FrmMain
4 | {
5 | ///
6 | /// 必需的设计器变量。
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// 清理所有正在使用的资源。
12 | ///
13 | /// 如果应释放托管资源,为 true;否则为 false。
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows 窗体设计器生成的代码
24 |
25 | ///
26 | /// 设计器支持所需的方法 - 不要
27 | /// 使用代码编辑器修改此方法的内容。
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.SuspendLayout();
32 | //
33 | // FrmMain
34 | //
35 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
37 | this.ClientSize = new System.Drawing.Size(600, 465);
38 | this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
39 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
40 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
41 | this.Name = "FrmMain";
42 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
43 | this.Text = "FrmMain";
44 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmMain_FormClosing);
45 | this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.frmMain_MouseClick);
46 | this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.frmMain_MouseDown);
47 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.frmMain_MouseMove);
48 | this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.frmMain_MouseUp);
49 | this.ResumeLayout(false);
50 |
51 | }
52 |
53 | #endregion
54 | }
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/FrmMain.cs:
--------------------------------------------------------------------------------
1 | #region 文档说明
2 | /* ******************************************************************************************************
3 | * 文档作者:无闻
4 | * 创建日期:2013 年 10 月 16 日
5 | * 文档用途:七牛云盘主窗口
6 | * ------------------------------------------------------------------------------------------------------
7 | * 修改记录:
8 | * ------------------------------------------------------------------------------------------------------
9 | * 参考文献:
10 | * ******************************************************************************************************/
11 | #endregion
12 |
13 | #region 命名空间引用
14 | using System;
15 | using System.IO;
16 | using System.Net;
17 | using System.Text;
18 | using System.Windows.Forms;
19 | using System.Drawing;
20 | using System.Threading;
21 | using System.Collections.Generic;
22 | using System.Diagnostics;
23 |
24 | using Qiniu.IO;
25 | using Qiniu.RPC;
26 | using Qiniu.RS;
27 | using Qiniu.RSF;
28 | using Qiniu.Conf;
29 |
30 | using Newtonsoft.Json;
31 |
32 | using CharmCommonMethod;
33 | using CharmControlLibrary;
34 | #endregion
35 |
36 | namespace QiNiuDrive
37 | {
38 | #region 枚举
39 | ///
40 | /// 过滤类型:前缀,后缀,全匹配
41 | ///
42 | public enum FilterType
43 | {
44 | ///
45 | /// 过滤类型:包含
46 | ///
47 | Contain,
48 | ///
49 | /// 过滤类型:前缀
50 | ///
51 | Prefix,
52 | ///
53 | /// 过滤类型:后缀
54 | ///
55 | Suffix,
56 | ///
57 | /// 过滤类型:全匹配
58 | ///
59 | FullMatch
60 | }
61 | #endregion
62 |
63 | #region 结构
64 | ///
65 | /// 同步文件
66 | ///
67 | public struct SyncFile
68 | {
69 | public string Name;
70 | public long Timestamp;
71 |
72 | public SyncFile(string name, long timestamp)
73 | {
74 | this.Name = name;
75 | this.Timestamp = timestamp;
76 | }
77 | }
78 |
79 | ///
80 | /// 过滤器
81 | ///
82 | public struct Filter
83 | {
84 | public FilterType Type;
85 | public string Name;
86 |
87 | public Filter(string name, FilterType type)
88 | {
89 | this.Name = name;
90 | this.Type = type;
91 | }
92 | }
93 |
94 | ///
95 | /// 修改文件
96 | ///
97 | public struct ChangeFile
98 | {
99 | public string OldName;
100 | public string NewName;
101 |
102 | public ChangeFile(string oldName, string newName)
103 | {
104 | this.OldName = oldName;
105 | this.NewName = newName;
106 | }
107 | }
108 | #endregion
109 |
110 | // 七牛云盘主窗口
111 | public partial class FrmMain : Form
112 | {
113 | #region 常量
114 | private const string APP_NAME = "七牛云盘"; // 软件名称
115 | private const string APP_VER = "v0.1.5"; // 软件版本
116 | private const int UPDATE_VERSION = 201312040; // 更新版本
117 | public const string APP_CFG_PATH = "Config\\app.ini"; // 软件配置路径
118 | private const int TITLE_HEIGHT = 30; // 标题栏高度
119 | private const int MENU_WIDTH = 90; // 菜单栏宽度
120 | #endregion
121 |
122 | #region 字段
123 | #region 主窗口
124 | // * 私有字段 *
125 | private string[] mMenuNames; // 菜单项名称
126 | private int mMenuSelectedIndex; // 菜单栏现行选中项索引
127 | private string mStatusText; // 状态文本
128 | private bool mIsLoadFinished; // 指示程序是否加载完毕
129 | private bool mIsUpdateChecked; // 指示是否完成检查更新
130 |
131 | // * 用户控件 *
132 | private List mCharmControls; // Charm 控件集合
133 | private readonly ToolTip mToolTip = new ToolTip(); // 工具提示文本控件
134 | private NotifyIcon mNotifyIcon; // 托盘图标
135 | private CharmButton mBtnApply; // 应用按钮
136 | private CharmMenu mTaryMenu; // 托盘菜单
137 |
138 | // * 七牛 *
139 | private RSFClient mRsfClient;
140 | private RSClient mRsClient;
141 | private List mServerFileList; // 服务器文件列表
142 | private List mLocalFileList; // 本地文件列表
143 | private List mLocalFileCache; // 本地文件缓存
144 | private List mChangeFileList; // 修改文件列表
145 | private PutRet mPutRet; // 上传返回结果
146 | #endregion
147 |
148 | #region 同步设置
149 | // * 私有字段 *
150 | private bool mIsVaildSyncDir; // 指示是否设置有效同步目录
151 | private bool mIsHasKeys; // 指示是否填写密钥
152 | private bool mIsVaildKeys; // 指示密钥是否有效
153 | private bool mIsVaildBucket; // 指示空间名称是否有效
154 | private bool mIsNeedVerifyAuth; // 指示是否需要验证授权
155 | private string mSyncDir; // 同步目录
156 | private int mSyncCycle; // 同步周期
157 | private string mBucket = string.Empty; // 空间名称
158 | private bool mIsPrivateBucket; // 指示是否为私有空间
159 | private string mPrefix = string.Empty; // 同步前缀
160 | private bool mIsCaptureChanges; // 指示是否监控变动
161 | private bool mIsDonePut; // 指示是否完成上传
162 | private bool mIsSyncNow; // 指示是否立即同步
163 | private bool mIsSyncing; // 指示是否正在同步
164 | private FileSystemWatcher mFileWatcher; // 文件监视器
165 |
166 | // * 用户控件 *
167 | private List mSyncSettingControls; // 同步设置控件集合
168 | // 0-同步目录文本框;1-同步周期文本框;2-AccessKey 文本框;3-SecretKey 文本框;4-空间名称文本框;5-同步前缀文本框
169 | private List mSyncSettingCharmControls; // 同步设置面板 Charm 控件集合
170 | // 4-私有空间检查框;5-监控变动检查框
171 | #endregion
172 |
173 | #region 高级设置
174 | // * 私有字段 *
175 | private List mFilterList; // 过滤器列表
176 |
177 | // * 用户控件 *
178 | private readonly FrmFilterTester mFrmFilterTester = new FrmFilterTester(); // 过滤规则测试窗口
179 | private List mAdvancedSettingCharmControls; // 高级设置面板 Charm 控件集合
180 | #endregion
181 |
182 | #region 关于
183 | private List mAboutCharmControls; // 关于面板 Charm 控件集合
184 | #endregion
185 | #endregion
186 |
187 | #region 窗体事件
188 | // 窗体构造方法
189 | public FrmMain()
190 | {
191 | // 初始化组件
192 | InitializeComponent();
193 | // 设置双缓冲模式
194 | this.SetStyle(ControlStyles.AllPaintingInWmPaint | //不擦除背景 ,减少闪烁
195 | ControlStyles.OptimizedDoubleBuffer | //双缓冲
196 | ControlStyles.UserPaint, //使用自定义的重绘事件,减少闪烁
197 | true);
198 | this.UpdateStyles();
199 | // 初始化设置
200 | InitializeSetting();
201 | }
202 |
203 | // 窗体即将关闭事件
204 | private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
205 | {
206 | // 释放系统资源
207 | mNotifyIcon.Dispose();
208 | }
209 |
210 | // 窗体鼠标单击事件
211 | private void frmMain_MouseClick(object sender, MouseEventArgs e)
212 | {
213 | #region 主面板事件
214 | // 根据菜单现行选中项索引判断绘制哪个面板
215 | switch (mMenuSelectedIndex)
216 | {
217 | case 0: // 同步设置
218 | CharmControl.MouseClickEvent(e, mSyncSettingCharmControls);
219 | break;
220 | case 1: // 高级设置
221 | CharmControl.MouseClickEvent(e, mAdvancedSettingCharmControls);
222 | break;
223 | case 3: // 关于
224 | CharmControl.MouseClickEvent(e, mAboutCharmControls);
225 | break;
226 | }
227 | #endregion
228 |
229 | // 调用事件
230 | CharmControl.MouseClickEvent(e, mCharmControls);
231 | }
232 |
233 | // 窗体鼠标按下事件
234 | private void frmMain_MouseDown(object sender, MouseEventArgs e)
235 | {
236 | // 只响应鼠标左键
237 | if (e.Button == MouseButtons.Left)
238 | {
239 | #region 菜单栏事件处理
240 | // 轮询菜单项
241 | for (int i = 0; i < mMenuNames.Length; i++)
242 | {
243 | // 判断是否为现行选中项且未被激活
244 | if (mMenuSelectedIndex == i || e.X <= 1 || e.X >= 91 ||
245 | e.Y <= TITLE_HEIGHT + 1 + i * 26 || e.Y >= TITLE_HEIGHT + 5 + (i + 1) * 26)
246 | continue;
247 |
248 | mMenuSelectedIndex = i; // 设置菜单栏现行选中项索引
249 | ShowPanels(); // 设置控件可见性
250 |
251 | // 重绘菜单栏区域
252 | this.Invalidate();
253 | //this.Invalidate(new Rectangle(1, 23, 90, mMenuNames.Length * 26));
254 | return;
255 | }
256 | #endregion
257 | }
258 |
259 | // 指示是否捕捉到事件
260 | bool isCaptureEvent = false;
261 |
262 | #region 主面板事件
263 | // 根据菜单现行选中项索引判断绘制哪个面板
264 | switch (mMenuSelectedIndex)
265 | {
266 | case 0: // 同步设置
267 | isCaptureEvent = CharmControl.MouseDownEvent(e, mSyncSettingCharmControls, this);
268 | break;
269 | case 1: // 高级设置
270 | isCaptureEvent = CharmControl.MouseDownEvent(e, mAdvancedSettingCharmControls, this);
271 | break;
272 | case 3: // 关于
273 | isCaptureEvent = CharmControl.MouseDownEvent(e, mAboutCharmControls, this);
274 | break;
275 | }
276 | #endregion
277 |
278 | // 判断之前是否已捕捉到事件
279 | if (isCaptureEvent) return;
280 |
281 | // 调用事件
282 | if (!CharmControl.MouseDownEvent(e, mCharmControls, this))
283 | APIOperation.MoveNoBorderForm(this, e);
284 | }
285 |
286 | // 窗体鼠标移动事件
287 | private void frmMain_MouseMove(object sender, MouseEventArgs e)
288 | {
289 | #region 主面板事件
290 | // 根据菜单现行选中项索引判断绘制哪个面板
291 | switch (mMenuSelectedIndex)
292 | {
293 | case 0: // 同步设置
294 | CharmControl.MouseMoveEvent(e, mSyncSettingCharmControls, this, mToolTip);
295 | break;
296 | case 1: // 高级设置
297 | CharmControl.MouseMoveEvent(e, mAdvancedSettingCharmControls, this, mToolTip);
298 | break;
299 | case 3: // 关于
300 | CharmControl.MouseMoveEvent(e, mAboutCharmControls, this, mToolTip);
301 | break;
302 | }
303 | #endregion
304 |
305 | // 调用事件
306 | CharmControl.MouseMoveEvent(e, mCharmControls, this, mToolTip);
307 | }
308 |
309 | // 窗体鼠标弹起事件
310 | private void frmMain_MouseUp(object sender, MouseEventArgs e)
311 | {
312 | #region 主面板事件
313 | // 根据菜单现行选中项索引判断绘制哪个面板
314 | switch (mMenuSelectedIndex)
315 | {
316 | case 0: // 同步设置
317 | CharmControl.MouseUpEvent(e, mSyncSettingCharmControls, this);
318 | break;
319 | case 1: // 高级设置
320 | CharmControl.MouseUpEvent(e, mAdvancedSettingCharmControls, this);
321 | break;
322 | case 3: // 关于
323 | CharmControl.MouseUpEvent(e, mAboutCharmControls, this);
324 | break;
325 | }
326 | #endregion
327 |
328 | // 调用事件
329 | CharmControl.MouseUpEvent(e, mCharmControls, this);
330 | }
331 | #endregion
332 |
333 | #region 重载事件
334 | // 控件重绘事件
335 | protected override void OnPaint(PaintEventArgs e)
336 | {
337 | // 获取绘制对象
338 | Graphics g = e.Graphics;
339 |
340 | // 绘制状态文本
341 | g.DrawString("(" + mStatusText + ")",
342 | new Font("微软雅黑", 10, FontStyle.Bold), Brushes.White, new Point(150, 6));
343 |
344 | #region 绘制菜单栏
345 | // 轮询菜单项
346 | for (int i = 0; i < mMenuNames.Length; i++)
347 | {
348 | // 判断是否为现行选中项
349 | if (mMenuSelectedIndex == i)
350 | {
351 | g.DrawImage(Properties.Resources.tab_button_setting_chosen, new Point(1, TITLE_HEIGHT + 1 + i * 26));
352 | g.DrawString(mMenuNames[i], this.Font, Brushes.White, new Point(20, TITLE_HEIGHT + 5 + i * 26));
353 | }
354 | else
355 | g.DrawString(mMenuNames[i], this.Font, Brushes.Black, new Point(20, TITLE_HEIGHT + 5 + i * 26));
356 | }
357 | #endregion
358 |
359 | #region 绘制主面板
360 | // 根据菜单现行选中项索引判断绘制哪个面板
361 | switch (mMenuSelectedIndex)
362 | {
363 | case 0: // 同步设置
364 | DrawSyncSettingPanel(g);
365 | CharmControl.PaintEvent(e.Graphics, mSyncSettingCharmControls);
366 | break;
367 | case 1: // 高级设置
368 | DrawAdvancedSettingPanel(g);
369 | CharmControl.PaintEvent(e.Graphics, mAdvancedSettingCharmControls);
370 | break;
371 | case 3: // 关于
372 | DrawAboutPanel(g);
373 | CharmControl.PaintEvent(e.Graphics, mAboutCharmControls);
374 | break;
375 | }
376 | #endregion
377 |
378 | // 绘制控件
379 | CharmControl.PaintEvent(e.Graphics, mCharmControls);
380 |
381 | // 重置重绘索引
382 | //mMultipleSettingRedrawIndex = 0;
383 | }
384 | #endregion
385 |
386 | #region 控件事件
387 | #region 主窗口
388 | // 最小化按钮被单击事件
389 | private void btnMin_MouseClick(object sender, MouseEventArgs e)
390 | {
391 | this.WindowState = FormWindowState.Minimized;
392 | }
393 |
394 | // 关闭按钮被单击事件
395 | private void btnClose_MouseClick(object sender, MouseEventArgs e)
396 | {
397 | CharmMessageBox msgbox;
398 | DialogResult result;
399 |
400 | if (mBtnApply.Enabled)
401 | {
402 | msgbox = new CharmMessageBox();
403 | result = msgbox.Show("您有修改未保存,是否保存?",
404 | "操作确认", MessageBoxButtons.YesNo, CharmMessageBoxIcon.Question);
405 |
406 | if (result == DialogResult.Yes)
407 | mBtnApply_MouseClick(sender, e);
408 | }
409 |
410 | msgbox = new CharmMessageBox();
411 | result = msgbox.Show("您确定要退出 " + APP_NAME + " 吗?",
412 | "退出确认", MessageBoxButtons.YesNo, CharmMessageBoxIcon.Question);
413 |
414 | if (result == DialogResult.Yes)
415 | this.Close();
416 | }
417 |
418 | // 托盘图标被单击事件
419 | private void mNotifyIcon_MouseClick(object sender, MouseEventArgs e)
420 | {
421 | // 判断鼠标按键
422 | if (e.Button == MouseButtons.Left)
423 | {
424 | // 显示主界面
425 | this.Visible = true;
426 | this.WindowState = FormWindowState.Normal;
427 | this.TopMost = true;
428 | this.TopMost = false;
429 | }
430 | else
431 | // 显示托盘菜单
432 | mTaryMenu.Show();
433 | }
434 |
435 | // 托盘菜单被单击事件
436 | private void mTaryMenu_MenuClick(int clickIndex)
437 | {
438 | // 判断用户单击的菜单项
439 | switch (clickIndex)
440 | {
441 | case 0: // 设置中心
442 | mNotifyIcon_MouseClick(new object(), new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0));
443 | break;
444 | case 1: // 打开同步目录
445 | if (mIsVaildSyncDir)
446 | Process.Start(mSyncDir);
447 | else
448 | {
449 | CharmMessageBox msgbox = new CharmMessageBox();
450 | msgbox.Show("您的操作由于以下原因导致失败:\n\n" +
451 | "- 未设置有效的同步目录",
452 | "操作失败", MessageBoxButtons.OK, CharmMessageBoxIcon.Error);
453 | }
454 | break;
455 | case 3: // 立即同步
456 | mIsSyncNow = true;
457 | break;
458 | case 5: // 关于
459 | mMenuSelectedIndex = 3;
460 | ShowPanels();
461 | this.Invalidate();
462 | mNotifyIcon_MouseClick(new object(), new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0));
463 | break;
464 | case 6: // 退出
465 | btnClose_MouseClick(new object(), new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0));
466 | break;
467 | }
468 | }
469 |
470 | // 确定按钮被单击事件
471 | private void btnMakeChange_MouseClick(object sender, MouseEventArgs e)
472 | {
473 | if (mBtnApply.Enabled)
474 | mBtnApply_MouseClick(sender, e);
475 |
476 | this.Visible = false;
477 | }
478 |
479 | // 取消按钮被单击事件
480 | private void btnCancel_MouseClick(object sender, MouseEventArgs e)
481 | {
482 | if (mBtnApply.Enabled)
483 | {
484 | mBtnApply.Enabled = false;
485 | mIsLoadFinished = false;
486 | LoadLocalSetting();
487 | }
488 | this.Visible = false;
489 | }
490 |
491 | // 应用按钮被单击事件
492 | private void mBtnApply_MouseClick(object sender, MouseEventArgs e)
493 | {
494 | mBtnApply.Enabled = false;
495 | this.Invalidate(mBtnApply.ClientRectangle); // 重绘控件
496 |
497 | #region 同步设置
498 | IniOperation.WriteValue(APP_CFG_PATH, "setting", "sync_dir", ((CharmTextBox)mSyncSettingControls[0]).Text);
499 | IniOperation.WriteValue(APP_CFG_PATH, "setting", "sync_cycle", ((CharmTextBox)mSyncSettingControls[1]).Text);
500 | IniOperation.WriteValue(APP_CFG_PATH, "setting", "bucket", ((CharmTextBox)mSyncSettingControls[4]).Text);
501 | IniOperation.WriteValue(APP_CFG_PATH, "setting", "private_bucket", (mSyncSettingCharmControls[4]).Checked.ToString());
502 | IniOperation.WriteValue(APP_CFG_PATH, "setting", "prefix", ((CharmTextBox)mSyncSettingControls[5]).Text);
503 | IniOperation.WriteValue(APP_CFG_PATH, "setting", "capture_changes", (mSyncSettingCharmControls[5]).Checked.ToString());
504 |
505 | string accessKey = ((CharmTextBox)mSyncSettingControls[2]).Text;
506 | string secretKey = ((CharmTextBox)mSyncSettingControls[3]).Text;
507 |
508 | if (accessKey.Length > 0 && secretKey.Length > 0)
509 | {
510 | string keys = BasicMethod.DesEncrypt(accessKey, "QWERTYUI") + "|" +
511 | BasicMethod.DesEncrypt(secretKey, "ASDFGHJK");
512 | StreamWriter sw = new StreamWriter("Config/KEY");
513 | sw.Write(keys);
514 | sw.Close();
515 | }
516 | #endregion
517 |
518 | mIsLoadFinished = false;
519 | LoadLocalSetting();
520 |
521 | if (!mIsUpdateChecked || !mIsVaildSyncDir || !mIsHasKeys)
522 | {
523 | RedrawStatusText("正在校验修改...");
524 | return;
525 | }
526 |
527 | RedrawStatusText("正在校验修改...");
528 | mIsNeedVerifyAuth = true;
529 | }
530 | #endregion
531 |
532 | #region 同步设置
533 | // 同步目录文本框文本改变事件(同步周期、AccessKey、SecretKey、空间名称文本框也关联此事件)
534 | private void txtSyncDir_TextChanged(object sender, EventArgs e)
535 | {
536 | // 首次启动时程序数据加载时不作响应
537 | if (!mIsLoadFinished || mBtnApply.Enabled) return;
538 |
539 | mBtnApply.Enabled = true;
540 | this.Invalidate(mBtnApply.ClientRectangle); // 重绘控件
541 | }
542 |
543 | // 浏览路径按钮被单击事件
544 | private void btnViewPath_MouseClick(object sender, MouseEventArgs e)
545 | {
546 | // 创建并实例化文件浏览对话框
547 | FolderBrowserDialog folderBrowserFialog = new FolderBrowserDialog
548 | {
549 | Description = "请选择同步目录",
550 | SelectedPath = ((CharmTextBox)mSyncSettingControls[0]).Text
551 | };
552 |
553 | // 显示对话框并判断用户是否指定新的目录
554 | if (folderBrowserFialog.ShowDialog() == DialogResult.OK)
555 | ((CharmTextBox)mSyncSettingControls[0]).Text = folderBrowserFialog.SelectedPath; // 用户指定新的目录
556 | }
557 |
558 | // 私有空间检查框被单击事件(监控变动检查框也关联此事件)
559 | private void chkPrivateBucket_MouseClick(object sender, MouseEventArgs e)
560 | {
561 | // 首次启动时程序数据加载时不作响应
562 | if (!mIsLoadFinished || mBtnApply.Enabled) return;
563 |
564 | mBtnApply.Enabled = true;
565 | this.Invalidate(mBtnApply.ClientRectangle); // 重绘控件
566 | }
567 |
568 | // 立即同步按钮被单击事件
569 | private void btnSyncNow_MouseClick(object sender, MouseEventArgs e)
570 | {
571 | mIsSyncNow = true;
572 | }
573 |
574 | // 查看 AccessKey 按钮被单击事件
575 | private void btnViewAccessKey_MouseClick(object sender, MouseEventArgs e)
576 | {
577 | CharmMessageBox msgbox = new CharmMessageBox();
578 | string accessKey = ((CharmTextBox)mSyncSettingControls[2]).Text;
579 | if (accessKey.Length == 0)
580 | msgbox.Show("尊敬的用户您好:\n" +
581 | "您尚未填写任何 AccessKey,请到七牛开发者中心获取!",
582 | "查看密钥", MessageBoxButtons.OK, CharmMessageBoxIcon.Warning);
583 | else
584 | msgbox.Show("尊敬的用户您好:\n" +
585 | "以下为您的 AccessKey,请妥善保管!\n\n" + accessKey,
586 | "查看密钥");
587 | }
588 |
589 | // 查看 SecretKey 按钮被单击事件
590 | private void btnViewSecretKey_MouseClick(object sender, MouseEventArgs e)
591 | {
592 | CharmMessageBox msgbox = new CharmMessageBox();
593 | string secretKey = ((CharmTextBox)mSyncSettingControls[3]).Text;
594 | if (secretKey.Length == 0)
595 | msgbox.Show("尊敬的用户您好:\n" +
596 | "您尚未填写任何 SecretKey,请到七牛开发者中心获取!",
597 | "查看密钥", MessageBoxButtons.OK, CharmMessageBoxIcon.Warning);
598 | else
599 | msgbox.Show("尊敬的用户您好:\n" +
600 | "以下为您的 SecretKey,请妥善保管!\n\n" + secretKey,
601 | "查看密钥");
602 | }
603 |
604 | // 七牛开发平台链接标签鼠标单击事件
605 | private static void lblQiniuOpen_MouseClick(object sender, MouseEventArgs e)
606 | {
607 | Process.Start("https://portal.qiniu.com/");
608 | }
609 |
610 | // 文件监视器捕捉到重命名事件
611 | private void mFileWatcher_Renamed(object source, RenamedEventArgs e)
612 | {
613 | if (mIsSyncing || !mIsCaptureChanges) return;
614 |
615 | // 判断是否为目录改名
616 | if (Directory.Exists(e.FullPath))
617 | DirectoryRenameEvent(e.OldFullPath, e.FullPath);
618 | else
619 | FileRenameEvent(e.OldName, e.Name);
620 | }
621 | #endregion
622 |
623 | #region 高级设置
624 | // 重载过滤规则按钮被单击事件
625 | private void btnReloadFilter_MouseClick(object sender, MouseEventArgs e)
626 | {
627 | LoadFilterList(mFilterList);
628 | RedrawStatusText("过滤规则已重载");
629 | }
630 |
631 | // 过滤规则测试按钮被单击事件
632 | private void btnTestFilter_MouseClick(object sender, MouseEventArgs e)
633 | {
634 | mFrmFilterTester.Show();
635 | }
636 | #endregion
637 |
638 | #region 关于
639 | // GitHub 链接标签鼠标单击事件
640 | private static void lblGithub_MouseClick(object sender, MouseEventArgs e)
641 | {
642 | Process.Start("https://github.com/Unknwon/qiniudrive");
643 | }
644 |
645 | // 新浪链接标签鼠标单击事件
646 | private static void lblSina_MouseClick(object sender, MouseEventArgs e)
647 | {
648 | Process.Start("http://weibo.com/Obahua");
649 | }
650 | #endregion
651 | #endregion
652 |
653 | #region 线程
654 | // 全局更新检查线程
655 | private void GlobalUpdateCheck()
656 | {
657 | // 检查更新
658 | RedrawStatusText("正在检查更新...");
659 |
660 | try
661 | {
662 | WebClient wb = new WebClient { Proxy = null };
663 | wb.Headers.Add("Cache-Control", "no-cache");
664 | var dict =
665 | JsonConvert.DeserializeObject>(
666 | wb.DownloadString("https://raw.github.com/Unknwon/qiniudrive/master/VERSION.json"));
667 | if (UPDATE_VERSION < (int)dict["version"])
668 | {
669 | CharmMessageBox msgbox = new CharmMessageBox();
670 | DialogResult result = msgbox.Show("尊敬的用户您好:\n\n" +
671 | "感谢您使用 " + APP_NAME + ",更高版本已经发布。\n\n" +
672 | "为了获得更好的用户体验,建议您立即更新!\n\n" +
673 | "请到 github.com/Unknwon/qiniudrive 获取最新下载地址。\n\n" +
674 | "或单击 是 自动跳转到下载页面。",
675 | "版本升级", MessageBoxButtons.YesNo);
676 | if (result == DialogResult.Yes)
677 | Process.Start(dict["download_url"]);
678 | }
679 | }
680 | catch (Exception e)
681 | {
682 | CharmMessageBox msgbox = new CharmMessageBox();
683 | msgbox.Show("检查更新失败:\n\n" +
684 | e.Message,
685 | "错误提示", MessageBoxButtons.OK, CharmMessageBoxIcon.Error);
686 | }
687 |
688 | // 初始化七牛服务
689 | mRsfClient = new RSFClient(mBucket);
690 | mRsClient = new RSClient();
691 | mServerFileList = new List();
692 | mLocalFileList = new List();
693 | mLocalFileCache = new List();
694 | mChangeFileList = new List();
695 |
696 | // 检查授权及其它设置
697 | VerifyAuth();
698 | RedrawStatusText("正在连接服务器...");
699 |
700 | mIsUpdateChecked = true;
701 |
702 | int count = 0;
703 |
704 | while (true)
705 | {
706 | #if(!DEBUG)
707 | //内存释放
708 | Process proc = Process.GetCurrentProcess();
709 | proc.MaxWorkingSet = Process.GetCurrentProcess().MaxWorkingSet;
710 | proc.Dispose();
711 | #endif
712 | if (((mSyncCycle != 0 && count % mSyncCycle == 0) || mIsSyncNow) &&
713 | mIsVaildKeys && mIsVaildBucket && mIsVaildSyncDir)
714 | {
715 | count = 0;
716 | mIsSyncing = true;
717 | Sync();
718 | mIsSyncNow = false;
719 | mIsSyncing = false;
720 | mServerFileList.Clear();
721 | mLocalFileList.Clear();
722 | }
723 |
724 | // 检查是否需要验证授权
725 | if (mIsNeedVerifyAuth)
726 | {
727 | mIsNeedVerifyAuth = false;
728 | VerifyAuth();
729 | RedrawStatusText("等待同步");
730 | }
731 |
732 | count++;
733 | Thread.Sleep(1000);
734 | }
735 | // ReSharper disable once FunctionNeverReturns
736 | }
737 | #endregion
738 |
739 | #region 方法
740 | // 初始化设置
741 | private void InitializeSetting()
742 | {
743 | // 设置窗体属性
744 | this.Icon = Properties.Resources.icon;
745 | this.Text = APP_NAME;
746 |
747 | // 绘制窗体背景
748 | DrawFormBackground();
749 |
750 | // 初始化菜单栏
751 | mMenuNames = new[] { "同步设置", "高级设置", "网络设置", "关于" };
752 |
753 | #region 创建窗体组件
754 | // 创建最小化系统按钮
755 | CharmSysButton btnMin = new CharmSysButton
756 | {
757 | SysButtonType = SysButtonType.Minimum,
758 | ToolTipText = "最小化",
759 | Location = new Point(this.Width - 75, 1)
760 | };
761 | // 创建关闭系统按钮
762 | CharmSysButton btnClose = new CharmSysButton
763 | {
764 | SysButtonType = SysButtonType.Close,
765 | ToolTipText = "关闭",
766 | Location = new Point(this.Width - 44, 1)
767 | };
768 |
769 | // 创建确定按钮
770 | CharmButton btnMakeChange = new CharmButton
771 | {
772 | ButtonType = ButtonType.Classic_Size_08223,
773 | Text = "确 定",
774 | Location = new Point(300, 430)
775 | };
776 | // 创建取消按钮
777 | CharmButton btnCancel = new CharmButton
778 | {
779 | ButtonType = ButtonType.Classic_Size_08223,
780 | Text = "取 消",
781 | Location = new Point(390, 430)
782 | };
783 | // 创建应用按钮
784 | mBtnApply = new CharmButton
785 | {
786 | ButtonType = ButtonType.Classic_Size_08223,
787 | Text = "应 用",
788 | Location = new Point(480, 430),
789 | Enabled = false
790 | };
791 |
792 | // 创建控件集合
793 | mCharmControls = new List { btnMin, btnClose, btnMakeChange, btnCancel, mBtnApply };
794 |
795 | // 关联控件事件
796 | btnMin.MouseClick += btnMin_MouseClick;
797 | btnClose.MouseClick += btnClose_MouseClick;
798 | btnMakeChange.MouseClick += btnMakeChange_MouseClick;
799 | btnCancel.MouseClick += btnCancel_MouseClick;
800 | mBtnApply.MouseClick += mBtnApply_MouseClick;
801 | #endregion
802 |
803 | #region 创建主体区域
804 | // 创建同步设置面板
805 | CreateSyncSettingPanel();
806 | // 创建高级设置面板
807 | CreateAdvancedSettingPanel();
808 | // 创建关于面板
809 | CreateAboutPanel();
810 | #endregion
811 |
812 | // 加载本地设置
813 | LoadLocalSetting();
814 |
815 | // 设置初始参数
816 | if (!mIsHasKeys)
817 | mStatusText = "未填写密钥";
818 | else if (!mIsVaildSyncDir)
819 | mStatusText = "无效的同步目录";
820 | else
821 | mStatusText = "正在初始化...";
822 |
823 | #region 创建菜单
824 | // 创建托盘菜单
825 | mTaryMenu = new CharmMenu();
826 | mTaryMenu.AddItem("设置中心", MenuItemType.TextItem,
827 | new Bitmap(Properties.Resources.logo, 16, 16));
828 | mTaryMenu.AddItem("打开同步目录", MenuItemType.TextItem);
829 | mTaryMenu.AddItem("", MenuItemType.Spliter);
830 | mTaryMenu.AddItem("立即同步", MenuItemType.TextItem);
831 | mTaryMenu.AddItem("", MenuItemType.Spliter);
832 | mTaryMenu.AddItem("关于", MenuItemType.TextItem);
833 | mTaryMenu.AddItem("退出", MenuItemType.TextItem);
834 |
835 | // 关联控件事件
836 | mTaryMenu.MenuClick += mTaryMenu_MenuClick;
837 | #endregion
838 |
839 | #region 创建托盘图标
840 | // 创建托盘图标
841 | mNotifyIcon = new NotifyIcon
842 | {
843 | Icon = Properties.Resources.icon,
844 | Text = APP_NAME + "\n" + mStatusText,
845 | Visible = true
846 | };
847 |
848 | // 关联控件事件
849 | mNotifyIcon.MouseClick += mNotifyIcon_MouseClick;
850 | #endregion
851 |
852 | #region 创建服务线程
853 | // 启动全局更新检查线程
854 | Thread globalUpdateCheckThread = new Thread(GlobalUpdateCheck) { IsBackground = true };
855 | globalUpdateCheckThread.Start();
856 | #endregion
857 | }
858 |
859 | // 绘制窗体背景
860 | private void DrawFormBackground()
861 | {
862 | // 加载窗体背景图像资源
863 | Bitmap imgBkg = new Bitmap(this.Width, this.Height);
864 |
865 | #region 绘制窗体内容
866 | using (Graphics g = Graphics.FromImage(imgBkg))
867 | {
868 | // 设置参数
869 | const int bottomHeight = 45;
870 |
871 | // 绘制标题栏背景
872 | SolidBrush sb = new SolidBrush(Color.DodgerBlue);
873 | g.FillRectangle(sb, 0, 0, this.Width, TITLE_HEIGHT);
874 | // 绘制窗口图标
875 | g.DrawImage(Properties.Resources.logo, new Rectangle(6, 5, 20, 20));
876 | // 绘制窗口标题
877 | g.DrawString("设置中心 - 七牛云盘",
878 | new Font("微软雅黑", 10, FontStyle.Bold), Brushes.White, new Point(30, 6));
879 | // 绘制竖线
880 | g.DrawLine(Pens.DarkGray, MENU_WIDTH + 1, TITLE_HEIGHT, 91, this.Height);
881 | // 绘制菜单区
882 | sb.Color = Color.FromArgb(100, Color.LightGray);
883 | g.FillRectangle(sb, 1, TITLE_HEIGHT, 90, this.Height);
884 | // 绘制主面板区
885 | sb.Color = Color.FromArgb(255, Color.WhiteSmoke);
886 | g.FillRectangle(sb, MENU_WIDTH + 2, TITLE_HEIGHT, this.Width - MENU_WIDTH - 4, this.Height - TITLE_HEIGHT - bottomHeight);
887 | // 绘制横线
888 | g.DrawLine(Pens.DarkGray, MENU_WIDTH + 2, this.Height - bottomHeight, this.Width - 2, this.Height - bottomHeight);
889 | // 绘制按钮区
890 | sb.Color = Color.FromArgb(70, Color.LightGray);
891 | g.FillRectangle(sb, MENU_WIDTH + 2, this.Height - bottomHeight, this.Width - 2, bottomHeight);
892 | // 绘制边框
893 | g.DrawRectangle(new Pen(Color.DimGray), 0, 0, this.Width - 1, this.Height - 1);
894 | }
895 | #endregion
896 |
897 | // 设置背景资源
898 | this.BackgroundImage = new Bitmap(imgBkg);
899 |
900 | // 释放系统资源
901 | imgBkg.Dispose();
902 | }
903 |
904 | // 加载本地设置
905 | private void LoadLocalSetting()
906 | {
907 | // 判断是否为初次运行程序
908 | if (!Directory.Exists("Config"))
909 | Directory.CreateDirectory("Config");
910 |
911 | mIsVaildKeys = true;
912 | mIsVaildBucket = true;
913 |
914 | #region 同步设置
915 | // 获取同步目录
916 | mSyncDir = IniOperation.ReadValue(APP_CFG_PATH, "setting", "sync_dir");
917 | if (mSyncDir.Length > 0 && Directory.Exists(mSyncDir))
918 | {
919 | ((CharmTextBox)mSyncSettingControls[0]).Text = mSyncDir;
920 |
921 | // 初始化文件监视器
922 | if (mFileWatcher == null)
923 | {
924 | mFileWatcher = new FileSystemWatcher { IncludeSubdirectories = true };
925 | mFileWatcher.Renamed += mFileWatcher_Renamed;
926 | }
927 |
928 | mFileWatcher.Path = mSyncDir;
929 | mFileWatcher.EnableRaisingEvents = true;
930 | mIsVaildSyncDir = true;
931 | }
932 | else
933 | {
934 | if (mFileWatcher != null)
935 | mFileWatcher.Dispose();
936 | mIsVaildSyncDir = false;
937 | }
938 |
939 | // 获取同步周期
940 | try
941 | {
942 | mSyncCycle = Convert.ToInt32(IniOperation.ReadValue(APP_CFG_PATH, "setting", "sync_cycle"));
943 | }
944 | catch
945 | {
946 | mSyncCycle = 60;
947 | }
948 |
949 | if (mSyncCycle < 10)
950 | mSyncCycle = 10;
951 | ((CharmTextBox)mSyncSettingControls[1]).Text = Convert.ToString(mSyncCycle);
952 |
953 | // 获取空间名称
954 | mBucket = IniOperation.ReadValue(APP_CFG_PATH, "setting", "bucket");
955 | if (mBucket.Length > 0)
956 | ((CharmTextBox)mSyncSettingControls[4]).Text = mBucket;
957 | else
958 | mIsVaildBucket = false;
959 | // 私有空间
960 | if (IniOperation.ReadValue(APP_CFG_PATH, "setting", "private_bucket").Equals("True"))
961 | {
962 | mIsPrivateBucket = true;
963 | mSyncSettingCharmControls[4].Checked = true;
964 | }
965 | else
966 | {
967 | mIsPrivateBucket = false;
968 | mSyncSettingCharmControls[4].Checked = false;
969 | }
970 |
971 | // 获取同步前缀
972 | mPrefix = IniOperation.ReadValue(APP_CFG_PATH, "setting", "prefix");
973 | if (mPrefix.Length > 0)
974 | ((CharmTextBox)mSyncSettingControls[5]).Text = mBucket;
975 |
976 | // 监控变动
977 | if (IniOperation.ReadValue(APP_CFG_PATH, "setting", "capture_changes").Equals("True"))
978 | {
979 | mIsCaptureChanges = true;
980 | mSyncSettingCharmControls[5].Checked = true;
981 | }
982 | else
983 | {
984 | mIsCaptureChanges = false;
985 | mSyncSettingCharmControls[5].Checked = false;
986 | }
987 |
988 |
989 | // 获取密钥
990 | if (File.Exists("Config/KEY"))
991 | {
992 | StreamReader sr = new StreamReader("Config/KEY");
993 | string[] keys = sr.ReadToEnd().Split('|');
994 | sr.Close();
995 |
996 | if (keys.Length > 0)
997 | {
998 | Config.ACCESS_KEY = BasicMethod.DesDecrypt(keys[0], "QWERTYUI");
999 | Config.SECRET_KEY = BasicMethod.DesDecrypt(keys[1], "ASDFGHJK");
1000 | ((CharmTextBox)mSyncSettingControls[2]).Text = Config.ACCESS_KEY;
1001 | ((CharmTextBox)mSyncSettingControls[3]).Text = Config.SECRET_KEY;
1002 | mIsHasKeys = true;
1003 | }
1004 | }
1005 | #endregion
1006 |
1007 | #region 高级设置
1008 | if (mFilterList == null)
1009 | mFilterList = new List();
1010 | LoadFilterList(mFilterList);
1011 | #endregion
1012 |
1013 | mIsLoadFinished = true;
1014 | }
1015 |
1016 | // 加载过滤器列表
1017 | public static void LoadFilterList(List filterList)
1018 | {
1019 | if (!File.Exists("Config/filter.txt")) return;
1020 |
1021 | filterList.Clear();
1022 |
1023 | StreamReader sr = new StreamReader("Config/filter.txt");
1024 | string[] filters = sr.ReadToEnd().Split('\n');
1025 | sr.Close();
1026 |
1027 | foreach (string f in filters)
1028 | {
1029 | string name = f.TrimEnd('\r');
1030 | if (name.Length <= 0) continue;
1031 |
1032 | int index = name.IndexOf('*');
1033 | int lastIndex = name.LastIndexOf('*');
1034 |
1035 | // 判断过滤类型
1036 | if (index == 0 && lastIndex == name.Length - 1) // 包含
1037 | filterList.Add(new Filter(name.Substring(1, name.Length - 2), FilterType.Contain));
1038 | else if (index == 0) // 后缀
1039 | filterList.Add(new Filter(name.Substring(1), FilterType.Suffix));
1040 | else if (index == name.Length - 1) // 前缀
1041 | filterList.Add(new Filter(name.Substring(0, name.Length - 1), FilterType.Prefix));
1042 | else // 全匹配
1043 | filterList.Add(new Filter(name, FilterType.FullMatch));
1044 | }
1045 | }
1046 |
1047 | // 显示面板
1048 | private void ShowPanels()
1049 | {
1050 | // 设置面板相关控件的可见性
1051 | if (mMenuSelectedIndex == 0) // 同步设置
1052 | foreach (Control ctrl in mSyncSettingControls)
1053 | ctrl.Visible = true;
1054 | else
1055 | foreach (Control ctrl in mSyncSettingControls)
1056 | ctrl.Visible = false;
1057 | }
1058 |
1059 | // 重绘状态文本
1060 | private void RedrawStatusText(string text)
1061 | {
1062 | if (text.Length > 0)
1063 | mStatusText = text;
1064 |
1065 | if (!mIsHasKeys)
1066 | mStatusText = "未填写密钥";
1067 | else if (!mIsVaildSyncDir)
1068 | mStatusText = "无效的同步目录";
1069 | else if (!mIsVaildBucket)
1070 | mStatusText = "无效的空间名称";
1071 | else if (!mIsVaildKeys)
1072 | mStatusText = "无效的密钥";
1073 |
1074 | if (mStatusText.Length > 28)
1075 | mStatusText = mStatusText.Substring(0, 28) + "...";
1076 | this.Invalidate(new Rectangle(150, 6, 360, 24));
1077 | mNotifyIcon.Text = APP_NAME + "\n" + mStatusText;
1078 | }
1079 |
1080 | // 验证授权
1081 | private void VerifyAuth()
1082 | {
1083 | if (mBucket.Length <= 0) return;
1084 |
1085 | try
1086 | {
1087 | mRsfClient.ListPrefix(mBucket, "", "", 1);
1088 | }
1089 | catch (Exception e)
1090 | {
1091 | CheckException(e.Message);
1092 | }
1093 | }
1094 |
1095 | // 同步
1096 | private void Sync()
1097 | {
1098 | #region 文件重命名处理
1099 | if (mChangeFileList.Count > 0)
1100 | {
1101 | RedrawStatusText("正在移动文件...");
1102 |
1103 | // 处理改名文件
1104 | List eppList = new List();
1105 | foreach (ChangeFile cf in mChangeFileList)
1106 | // 判断文件是否依旧存在
1107 | if (File.Exists(mSyncDir + "\\" + cf.NewName))
1108 | {
1109 | eppList.Add(new EntryPathPair(mBucket,
1110 | cf.OldName.Replace("\\", "/"), cf.NewName.Replace("\\", "/")));
1111 | Console.WriteLine("增加移动文件:{0} {1}", cf.OldName.Replace("\\", "/"), cf.NewName.Replace("\\", "/"));
1112 | }
1113 | mRsClient.BatchMove(eppList.ToArray());
1114 | }
1115 | #endregion
1116 |
1117 | #region 文件删除处理
1118 | RedrawStatusText("正在对比文件...");
1119 |
1120 | // 获取本地文件列表
1121 | LoadLocalFiles("");
1122 |
1123 | //// 获取本地文件缓存
1124 | if (File.Exists("Data/file_cache_list"))
1125 | {
1126 | mLocalFileCache.Clear();
1127 |
1128 | StreamReader sr = new StreamReader("Data/file_cache_list");
1129 | string[] files = sr.ReadToEnd().Split('\n');
1130 | sr.Close();
1131 |
1132 | // 查找删除项
1133 | foreach (string f in files)
1134 | {
1135 | string cacheName = f.TrimEnd('\r');
1136 | // 判断文件是否被移动
1137 | if (cacheName.Length <= 0 || FindChangeFileIndex(cacheName) > -1) continue;
1138 |
1139 | cacheName = cacheName.Replace("\\", "/");
1140 | if (IsNeedFilter(cacheName)) continue;
1141 |
1142 | if (FindLocalFileIndex(cacheName) != -1)
1143 | {
1144 | mLocalFileCache.Add(cacheName);
1145 | continue;
1146 | }
1147 |
1148 | //RedrawStatusText("正在删除(上行)..." + cacheName);
1149 | //CallRet cr = mRsClient.Delete(new EntryPath(mBucket, cacheName));
1150 | //if (!cr.OK)
1151 | //{
1152 | // if (!cr.Exception.Message.Contains("612"))
1153 | // RedrawStatusText("删除失败..." + cacheName);
1154 | //}
1155 | //else
1156 | // Thread.Sleep(1000);
1157 | }
1158 | }
1159 |
1160 | mChangeFileList.Clear();
1161 | #endregion
1162 |
1163 | // 拉取服务器文件列表
1164 | bool isHasMore = true;
1165 | string marker = string.Empty;
1166 |
1167 | while (isHasMore)
1168 | {
1169 | DumpRet dr = mRsfClient.ListPrefix(mBucket, mPrefix, marker);
1170 |
1171 | // 存储文件到服务器文件列表
1172 | foreach (DumpItem item in dr.Items)
1173 | if (!IsNeedFilter(item.Key))
1174 | mServerFileList.Add(new SyncFile(item.Key, item.PutTime / 10000000));
1175 |
1176 | if (dr.Marker != null)
1177 | marker = dr.Marker;
1178 | else
1179 | isHasMore = false;
1180 | }
1181 |
1182 | List tmpCacheList = new List();
1183 | // 文件差异对比及消除
1184 | for (int i = 0; i < mServerFileList.Count; i++)
1185 | {
1186 | int index = FindLocalFileIndex(mServerFileList[i].Name);
1187 | string curServeFileName = mServerFileList[i].Name.Replace("/", "\\");
1188 | DateTime unixTime = new DateTime(1970, 1, 1).AddSeconds(mServerFileList[i].Timestamp);
1189 |
1190 | if (index == -1)
1191 | {
1192 | //RedrawStatusText("正在下载..." + mServerFileList[i].Name);
1193 | //if (!DownloadFile("http://" + mBucket + ".u.qiniudn.com/" + mServerFileList[i].Name,
1194 | // curServeFileName, unixTime))
1195 | // RedrawStatusText("下载失败..." + mServerFileList[i].Name);
1196 | }
1197 | else if (mServerFileList[i].Timestamp > mLocalFileList[index].Timestamp)
1198 | {
1199 | //RedrawStatusText("正在更新..." + mServerFileList[i].Name);
1200 | //if (!DownloadFile("http://" + mBucket + ".u.qiniudn.com/" + mServerFileList[i].Name,
1201 | // curServeFileName, unixTime))
1202 | // RedrawStatusText("更新失败..." + mServerFileList[i].Name);
1203 | mLocalFileList.RemoveAt(index);
1204 | }
1205 | else if (mServerFileList[i].Timestamp < mLocalFileList[index].Timestamp)
1206 | {
1207 | RedrawStatusText("正在上传..." + mServerFileList[i].Name);
1208 | mIsDonePut = false;
1209 | PutFile(mPrefix + mServerFileList[i].Name, mSyncDir + "\\" + mServerFileList[i].Name, true);
1210 | WaitUntilTure(mIsDonePut);
1211 | if (mPutRet.OK)
1212 | // ReSharper disable once PossibleLossOfFraction
1213 | File.SetLastWriteTimeUtc(mSyncDir + "\\" + mServerFileList[i].Name,
1214 | new DateTime(1970, 1, 1).AddSeconds(mRsClient.Stat(new EntryPath(mBucket, mServerFileList[i].Name)).PutTime / 10000000));
1215 | else
1216 | {
1217 | CheckException(mPutRet.Exception.Message);
1218 | RedrawStatusText("同步失败");
1219 | mNotifyIcon.ShowBalloonTip(1000, "同步失败", mPutRet.Exception.Message, ToolTipIcon.Error);
1220 | return;
1221 | }
1222 | mLocalFileList.RemoveAt(index);
1223 | }
1224 | else if (mServerFileList[i].Timestamp == mLocalFileList[index].Timestamp)
1225 | mLocalFileList.RemoveAt(index);
1226 | tmpCacheList.Add(curServeFileName);
1227 | }
1228 |
1229 | #region 本地完全差异文件上传
1230 | foreach (SyncFile sf in mLocalFileList)
1231 | {
1232 | // 存在缓存列表中说明已经上传过,因此属删除操作
1233 | if (FindLocalCacheIndex(sf.Name) > -1)
1234 | {
1235 | //RedrawStatusText("正在删除(下行)..." + sf.Name);
1236 | //File.Delete(mSyncDir + "\\" + sf.Name);
1237 | //continue;
1238 | }
1239 |
1240 | if (IsNeedFilter(sf.Name)) continue;
1241 |
1242 | RedrawStatusText("正在上载..." + sf.Name);
1243 | mIsDonePut = false;
1244 | PutFile(mPrefix + sf.Name.Replace("\\", "/"), mSyncDir + "\\" + sf.Name);
1245 | WaitUntilTure(mIsDonePut);
1246 | if (mPutRet.OK)
1247 | // ReSharper disable once PossibleLossOfFraction
1248 | File.SetLastWriteTimeUtc(mSyncDir + "\\" + sf.Name,
1249 | new DateTime(1970, 1, 1).AddSeconds(
1250 | mRsClient.Stat(new EntryPath(mBucket, sf.Name.Replace("\\", "/"))).PutTime / 10000000));
1251 | else
1252 | {
1253 | CheckException(mPutRet.Exception.Message);
1254 | RedrawStatusText("上载失败");
1255 | mNotifyIcon.ShowBalloonTip(1000, "上载失败", mPutRet.Exception.Message, ToolTipIcon.Error);
1256 | return;
1257 | }
1258 | tmpCacheList.Add(sf.Name);
1259 | }
1260 | #endregion
1261 |
1262 | // 保存本地文件缓存
1263 | StringBuilder sb = new StringBuilder();
1264 | foreach (string s in tmpCacheList)
1265 | sb.AppendLine(s);
1266 | if (sb.Length > 0)
1267 | {
1268 | StreamWriter sw = new StreamWriter("Data/file_cache_list");
1269 | sw.Write(sb.ToString());
1270 | sw.Close();
1271 | }
1272 |
1273 | RedrawStatusText("同步完成");
1274 | }
1275 |
1276 | // 判断是否需要过滤
1277 | private bool IsNeedFilter(string name)
1278 | {
1279 | if (mFilterList == null) return false;
1280 |
1281 | foreach (Filter f in mFilterList)
1282 | switch (f.Type)
1283 | {
1284 | case FilterType.Contain:
1285 | if (name.Contains(f.Name))
1286 | return true;
1287 | break;
1288 | case FilterType.Prefix:
1289 | if (name.StartsWith(f.Name))
1290 | return true;
1291 | break;
1292 | case FilterType.Suffix:
1293 | if (name.EndsWith(f.Name))
1294 | return true;
1295 | break;
1296 | case FilterType.FullMatch:
1297 | if (name.Equals(f.Name))
1298 | return true;
1299 | break;
1300 | }
1301 |
1302 | return false;
1303 | }
1304 |
1305 | // 加载本地文件(可用于递归操作)
1306 | private void LoadLocalFiles(string dirPrefix)
1307 | {
1308 | DateTime timeStamp = new DateTime(1970, 1, 1);
1309 |
1310 | // 检查文件
1311 | DirectoryInfo di = new DirectoryInfo(mSyncDir + dirPrefix);
1312 | foreach (FileInfo fi in di.GetFiles())
1313 | {
1314 | string fileName = (dirPrefix.TrimStart('\\') + "\\" + fi.Name).TrimStart('\\');
1315 |
1316 | // 忽略隐藏文件
1317 | if (fi.Attributes.ToString().Contains("Hidden"))
1318 | continue;
1319 |
1320 | fileName = fileName.Replace("\\", "/");
1321 | if (!IsNeedFilter(fileName))
1322 | mLocalFileList.Add(
1323 | new SyncFile(fileName, (fi.LastWriteTimeUtc.Ticks - timeStamp.Ticks) / 10000000));
1324 | else
1325 | Console.WriteLine("过滤 " + fileName);
1326 | }
1327 |
1328 | // 检查目录
1329 | foreach (DirectoryInfo subDi in di.GetDirectories())
1330 | LoadLocalFiles(dirPrefix + "\\" + subDi.Name);
1331 | }
1332 |
1333 | // 查找修改文件列表中的匹配项
1334 | private int FindChangeFileIndex(string name)
1335 | {
1336 | for (int i = 0; i < mChangeFileList.Count; i++)
1337 | if (name.Equals(mChangeFileList[i].OldName) ||
1338 | name.Equals(mChangeFileList[i].NewName))
1339 | return i;
1340 | return -1;
1341 | }
1342 |
1343 | // 查找本地文件列表中的匹配项
1344 | private int FindLocalFileIndex(string name)
1345 | {
1346 | for (int i = 0; i < mLocalFileList.Count; i++)
1347 | if (mLocalFileList[i].Name.Equals(name.TrimStart(mPrefix.ToCharArray())))
1348 | return i;
1349 | return -1;
1350 | }
1351 |
1352 | // 查找本地缓存列表中的匹配项
1353 | private int FindLocalCacheIndex(string name)
1354 | {
1355 | for (int i = 0; i < mLocalFileCache.Count; i++)
1356 | if (mLocalFileCache[i].Equals(name))
1357 | return i;
1358 | return -1;
1359 | }
1360 |
1361 | ///
1362 | /// 上传文件
1363 | ///
1364 | /// 文件名称
1365 | /// 本地文件名称
1366 | /// 是否覆盖
1367 | private void PutFile(string key, string fname, bool rewrite = false)
1368 | {
1369 | string scope = mBucket;
1370 | if (rewrite)
1371 | scope += ":" + key;
1372 |
1373 | string upToken = new PutPolicy(scope).Token();
1374 | IOClient client = new IOClient();
1375 | client.PutFinished += (o, ret) =>
1376 | {
1377 | mPutRet = ret;
1378 | mIsDonePut = true;
1379 | };
1380 | client.PutFile(upToken, key, fname, null);
1381 | }
1382 |
1383 | // 循环等待
1384 | private void WaitUntilTure(bool cond)
1385 | {
1386 | while (true)
1387 | {
1388 | if (cond)
1389 | return;
1390 | Thread.Sleep(100);
1391 | }
1392 | }
1393 |
1394 | // 检查异常
1395 | private void CheckException(string message)
1396 | {
1397 | mIsVaildKeys = !message.Contains("(401)");
1398 | mIsVaildBucket = !message.Contains("(631)") && !message.Contains("(599)");
1399 | }
1400 |
1401 | // 目录重命名事件
1402 | private void DirectoryRenameEvent(string oldPath, string newPath)
1403 | {
1404 | DirectoryInfo di = new DirectoryInfo(newPath);
1405 | foreach (FileInfo fi in di.GetFiles())
1406 | FileRenameEvent((oldPath + "\\" + fi.Name).Substring(mSyncDir.Length + 1),
1407 | (newPath + "\\" + fi.Name).Substring(mSyncDir.Length + 1));
1408 |
1409 | foreach (DirectoryInfo subDi in di.GetDirectories())
1410 | DirectoryRenameEvent(oldPath + "\\" + subDi.Name, subDi.FullName);
1411 | }
1412 |
1413 | // 文件重命名事件
1414 | private void FileRenameEvent(string oldPath, string newPath)
1415 | {
1416 | // 忽略 .tmp 结尾的文件
1417 | if (oldPath.ToLower().EndsWith(".tmp") || newPath.ToLower().EndsWith(".tmp")) return;
1418 |
1419 | int index = FindChangeFileIndex(oldPath);
1420 | if (index == -1)
1421 | mChangeFileList.Add(new ChangeFile(oldPath, newPath));
1422 | else
1423 | {
1424 | // 判断是否改回了原来的名字
1425 | if (!mChangeFileList[index].OldName.Equals(newPath))
1426 | mChangeFileList.Add(new ChangeFile(mChangeFileList[index].OldName, newPath));
1427 | mChangeFileList.RemoveAt(index);
1428 | }
1429 | Console.WriteLine("文件重命名:{0} {1}", oldPath, newPath);
1430 | }
1431 |
1432 | // 下载文件
1433 | private bool DownloadFile(string url, string key, DateTime unixTime)
1434 | {
1435 | // 判断是否为私有空间
1436 | if (mIsPrivateBucket)
1437 | {
1438 | string baseUrl = GetPolicy.MakeBaseUrl(mBucket + ".u.qiniudn.com", key);
1439 | url = GetPolicy.MakeRequest(baseUrl);
1440 | }
1441 |
1442 | string path = mSyncDir + "\\" + key;
1443 | // ReSharper disable once AssignNullToNotNullAttribute
1444 | Directory.CreateDirectory(Path.GetDirectoryName(path));
1445 | WebClient wb = new WebClient { Proxy = null };
1446 | try
1447 | {
1448 | wb.DownloadFile(url, path);
1449 | File.SetLastWriteTimeUtc(path, unixTime);
1450 | }
1451 | catch
1452 | {
1453 | return false;
1454 | }
1455 | return true;
1456 | }
1457 |
1458 | #region 创建面板方法
1459 | // 创建同步设置面板
1460 | private void CreateSyncSettingPanel()
1461 | {
1462 | // 创建同步目录文本框
1463 | CharmTextBox txtSyncDir = new CharmTextBox
1464 | {
1465 | Location = new Point(127 + MENU_WIDTH, 20 + TITLE_HEIGHT),
1466 | Width = 260
1467 | };
1468 | // 创建浏览路径按钮
1469 | CharmButton btnViewPath = new CharmButton
1470 | {
1471 | ButtonType = ButtonType.Classic_Size_08223,
1472 | Text = "浏览路径",
1473 | ForeColor = Color.DarkGreen,
1474 | Location = new Point(399 + MENU_WIDTH, 22 + TITLE_HEIGHT)
1475 | };
1476 |
1477 | // 创建同步周期文本框
1478 | CharmTextBox txtSyncCycle = new CharmTextBox
1479 | {
1480 | Location = new Point(127 + MENU_WIDTH, 55 + TITLE_HEIGHT),
1481 | Width = 50,
1482 | TextInputMode = InputMode.Integer,
1483 | MaxLength = 5,
1484 | TextAlign = HorizontalAlignment.Center
1485 | };
1486 |
1487 | // 创建空间名称文本框
1488 | CharmTextBox txtBucket = new CharmTextBox
1489 | {
1490 | Location = new Point(290 + MENU_WIDTH, 55 + TITLE_HEIGHT),
1491 | Width = 95,
1492 | };
1493 |
1494 | // 创建私有空间检查框
1495 | CharmCheckBox chkPrivateBucket = new CharmCheckBox
1496 | {
1497 | Location = new Point(400 + MENU_WIDTH, 57 + TITLE_HEIGHT),
1498 | Text = "私有空间"
1499 | };
1500 |
1501 | // 创建同步前缀文本框
1502 | CharmTextBox txtPrefix = new CharmTextBox
1503 | {
1504 | Location = new Point(150 + MENU_WIDTH, 90 + TITLE_HEIGHT),
1505 | Width = 95,
1506 | };
1507 |
1508 | // 创建监控变动检查框
1509 | CharmCheckBox chkCaptureChange = new CharmCheckBox
1510 | {
1511 | Location = new Point(260 + MENU_WIDTH, 92 + TITLE_HEIGHT),
1512 | Text = "监控目录文件变化"
1513 | };
1514 |
1515 | // 创建立即同步按钮
1516 | CharmButton btnSyncNow = new CharmButton
1517 | {
1518 | ButtonType = ButtonType.Classic_Size_08223,
1519 | Text = "立即同步",
1520 | ForeColor = Color.Purple,
1521 | Location = new Point(399 + MENU_WIDTH, 92 + TITLE_HEIGHT)
1522 | };
1523 |
1524 | // 创建 AccessKey 文本框
1525 | CharmTextBox txtAccessKey = new CharmTextBox
1526 | {
1527 | Location = new Point(127 + MENU_WIDTH, 160 + TITLE_HEIGHT),
1528 | Width = 260,
1529 | TextInputMode = InputMode.Password
1530 | };
1531 | // 创建查看 AccessKey 按钮
1532 | CharmButton btnViewAccessKey = new CharmButton
1533 | {
1534 | ButtonType = ButtonType.Classic_Size_08223,
1535 | Text = "查看密钥",
1536 | ForeColor = Color.Red,
1537 | Location = new Point(399 + MENU_WIDTH, 162 + TITLE_HEIGHT)
1538 | };
1539 |
1540 | // 创建 SecretKey 文本框
1541 | CharmTextBox txtSecretKey = new CharmTextBox
1542 | {
1543 | Location = new Point(127 + MENU_WIDTH, 195 + TITLE_HEIGHT),
1544 | Width = 260,
1545 | TextInputMode = InputMode.Password
1546 | };
1547 | // 创建查看 SecretKey 按钮
1548 | CharmButton btnViewSecretKey = new CharmButton
1549 | {
1550 | ButtonType = ButtonType.Classic_Size_08223,
1551 | Text = "查看密钥",
1552 | ForeColor = Color.Red,
1553 | Location = new Point(399 + MENU_WIDTH, 197 + TITLE_HEIGHT)
1554 | };
1555 |
1556 | // 创建七牛开发平台链接标签
1557 | CharmLinkLabel lblQiniuOpen = new CharmLinkLabel
1558 | {
1559 | Location = new Point(280, 265),
1560 | ForeColor = Color.Blue,
1561 | Text = "七牛云存储开发者平台"
1562 | };
1563 |
1564 | // 关联控件事件
1565 | txtSyncDir.TextChanged += txtSyncDir_TextChanged;
1566 | btnViewPath.MouseClick += btnViewPath_MouseClick;
1567 | txtSyncCycle.TextChanged += txtSyncDir_TextChanged;
1568 | txtBucket.TextChanged += txtSyncDir_TextChanged;
1569 | chkPrivateBucket.MouseClick += chkPrivateBucket_MouseClick;
1570 | lblQiniuOpen.MouseClick += lblQiniuOpen_MouseClick;
1571 | txtAccessKey.TextChanged += txtSyncDir_TextChanged;
1572 | btnViewAccessKey.MouseClick += btnViewAccessKey_MouseClick;
1573 | txtSecretKey.TextChanged += txtSyncDir_TextChanged;
1574 | btnViewSecretKey.MouseClick += btnViewSecretKey_MouseClick;
1575 | txtPrefix.TextChanged += txtSyncDir_TextChanged;
1576 | chkCaptureChange.MouseClick += chkPrivateBucket_MouseClick;
1577 | btnSyncNow.MouseClick += btnSyncNow_MouseClick;
1578 |
1579 | // 将控件添加到集合中
1580 | this.Controls.Add(txtSyncDir);
1581 | this.Controls.Add(txtSyncCycle);
1582 | this.Controls.Add(txtAccessKey);
1583 | this.Controls.Add(txtSecretKey);
1584 | this.Controls.Add(txtBucket);
1585 | this.Controls.Add(txtPrefix);
1586 |
1587 | // 创建同步设置面板控件集合
1588 | mSyncSettingControls = new List { txtSyncDir, txtSyncCycle, txtAccessKey, txtSecretKey, txtBucket, txtPrefix };
1589 | mSyncSettingCharmControls = new List { btnViewPath, lblQiniuOpen, btnViewAccessKey, btnViewSecretKey, chkPrivateBucket, chkCaptureChange, btnSyncNow };
1590 | }
1591 |
1592 | // 创建高级设置面板
1593 | private void CreateAdvancedSettingPanel()
1594 | {
1595 | // 创建重载过滤规则按钮
1596 | CharmButton btnReloadFilter = new CharmButton
1597 | {
1598 | ButtonType = ButtonType.Classic_Size_12425,
1599 | Text = "重载过滤规则",
1600 | ForeColor = Color.MediumPurple,
1601 | Location = new Point(85 + MENU_WIDTH, 50 + TITLE_HEIGHT)
1602 |
1603 | };
1604 | // 创建过滤规则测试按钮
1605 | CharmButton btnTestFilter = new CharmButton
1606 | {
1607 | ButtonType = ButtonType.Classic_Size_12425,
1608 | Text = "过滤规则测试",
1609 | ForeColor = Color.MediumSlateBlue,
1610 | Location = new Point(245 + MENU_WIDTH, 50 + TITLE_HEIGHT)
1611 |
1612 | };
1613 |
1614 | // 关联控件事件
1615 | btnReloadFilter.MouseClick += btnReloadFilter_MouseClick;
1616 | btnTestFilter.MouseClick += btnTestFilter_MouseClick;
1617 |
1618 | // 创建同步设置面板控件集合
1619 | mAdvancedSettingCharmControls = new List { btnReloadFilter, btnTestFilter };
1620 | }
1621 |
1622 | // 创建关于面板
1623 | private void CreateAboutPanel()
1624 | {
1625 | // 创建 GitHub 链接标签
1626 | CharmLinkLabel lblGithub = new CharmLinkLabel
1627 | {
1628 | Location = new Point(185, 145),
1629 | Font = new Font("微软雅黑", 10),
1630 | ForeColor = Color.Blue,
1631 | Text = "github.com/Unknwon/qiniudrive"
1632 | };
1633 |
1634 | // 创建新浪微博链接标签
1635 | CharmLinkLabel lblSina = new CharmLinkLabel
1636 | {
1637 | Location = new Point(330, 223),
1638 | Font = new Font("微软雅黑", 10),
1639 | ForeColor = Color.Blue,
1640 | Text = "@无闻Unknown"
1641 | };
1642 |
1643 | // 关联控件事件
1644 | lblGithub.MouseClick += lblGithub_MouseClick;
1645 | lblSina.MouseClick += lblSina_MouseClick;
1646 |
1647 | // 创建关于面板控件集合
1648 | mAboutCharmControls = new List { lblGithub, lblSina };
1649 | }
1650 | #endregion
1651 |
1652 | #region 绘制面板方法
1653 | // 绘制同步设置面板
1654 | private void DrawSyncSettingPanel(Graphics g)
1655 | {
1656 | // 同步目录
1657 | g.DrawString("云盘同步目录:", this.Font, Brushes.Black, 125, 25 + TITLE_HEIGHT);
1658 | // 同步周期
1659 | g.DrawString("同步时间周期:", this.Font, Brushes.Black, 125, 60 + TITLE_HEIGHT);
1660 | g.DrawString("秒", this.Font, Brushes.Black, 275, 60 + TITLE_HEIGHT);
1661 |
1662 | // 空间名称
1663 | g.DrawString("空间名称:", this.Font, Brushes.Black, 310, 60 + TITLE_HEIGHT);
1664 |
1665 | // 同步前缀
1666 | g.DrawString("当前设备同步前缀:", this.Font, Brushes.Black, 125, 95 + TITLE_HEIGHT);
1667 |
1668 | // 密钥管理
1669 | g.DrawString("密钥管理", this.Font, Brushes.Black, new Point(22 + MENU_WIDTH, 135 + TITLE_HEIGHT));
1670 | g.DrawLine(Pens.DarkGray, 90 + MENU_WIDTH, 145 + TITLE_HEIGHT, this.Width - 50, 145 + TITLE_HEIGHT);
1671 | g.DrawString("Access Key:", this.Font, Brushes.Black, 125, 165 + TITLE_HEIGHT);
1672 | g.DrawString("Secret Key:", this.Font, Brushes.Black, 125, 200 + TITLE_HEIGHT);
1673 | g.DrawString("申请注册七牛开发者帐号:", this.Font, Brushes.Black, 125, 235 + TITLE_HEIGHT);
1674 | }
1675 |
1676 | // 绘制高级设置面板
1677 | private void DrawAdvancedSettingPanel(Graphics g)
1678 | {
1679 | // 文件过滤
1680 | g.DrawString("文件过滤", this.Font, Brushes.Black, new Point(22 + MENU_WIDTH, 22 + TITLE_HEIGHT));
1681 | g.DrawLine(Pens.DarkGray, 90 + MENU_WIDTH, 32 + TITLE_HEIGHT, this.Width - 50, 32 + TITLE_HEIGHT);
1682 | }
1683 |
1684 | // 绘制关于面板
1685 | private static void DrawAboutPanel(Graphics g)
1686 | {
1687 | Font font = new Font("微软雅黑", 10, FontStyle.Bold);
1688 | // 基本说明
1689 | string intro = "本程序是基于七牛开放 API 构建的第三方同步程序\n" +
1690 | "七牛云存储官方不对本程序的任何行为负责。";
1691 | g.DrawString(intro, font, Brushes.Black, 185, 60);
1692 | // 开源说明
1693 | intro = "七牛云盘由 C# 编写并已开源在 GitHub 上:";
1694 | g.DrawString(intro, font, Brushes.Black, 185, 120);
1695 | // 版权说明
1696 | g.DrawImage(Properties.Resources.unknown, 190, 200, 64, 64);
1697 | //font = new Font("微软雅黑", 10);
1698 | intro = "程序作者:无闻\n" +
1699 | "新浪微博:\n" +
1700 | "个人博客:暂未开通";
1701 | g.DrawString(intro, font, Brushes.Black, 265, 205);
1702 | intro = "软件版本:" + APP_VER + " 版权所有 @ 2013 无闻";
1703 | g.DrawString(intro, font, Brushes.Black, 205, 285);
1704 | // 绘制七牛 LOGO
1705 | g.DrawImage(Properties.Resources.qiniu_logo, 190, 330, 290, 45);
1706 | }
1707 | #endregion
1708 | #endregion
1709 | }
1710 | }
1711 |
--------------------------------------------------------------------------------
/FrmMain.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction, and
10 | distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright
13 | owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all other entities
16 | that control, are controlled by, or are under common control with that entity.
17 | For the purposes of this definition, "control" means (i) the power, direct or
18 | indirect, to cause the direction or management of such entity, whether by
19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 | outstanding shares, or (iii) beneficial ownership of such entity.
21 |
22 | "You" (or "Your") shall mean an individual or Legal Entity exercising
23 | permissions granted by this License.
24 |
25 | "Source" form shall mean the preferred form for making modifications, including
26 | but not limited to software source code, documentation source, and configuration
27 | files.
28 |
29 | "Object" form shall mean any form resulting from mechanical transformation or
30 | translation of a Source form, including but not limited to compiled object code,
31 | generated documentation, and conversions to other media types.
32 |
33 | "Work" shall mean the work of authorship, whether in Source or Object form, made
34 | available under the License, as indicated by a copyright notice that is included
35 | in or attached to the work (an example is provided in the Appendix below).
36 |
37 | "Derivative Works" shall mean any work, whether in Source or Object form, that
38 | is based on (or derived from) the Work and for which the editorial revisions,
39 | annotations, elaborations, or other modifications represent, as a whole, an
40 | original work of authorship. For the purposes of this License, Derivative Works
41 | shall not include works that remain separable from, or merely link (or bind by
42 | name) to the interfaces of, the Work and Derivative Works thereof.
43 |
44 | "Contribution" shall mean any work of authorship, including the original version
45 | of the Work and any modifications or additions to that Work or Derivative Works
46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
47 | by the copyright owner or by an individual or Legal Entity authorized to submit
48 | on behalf of the copyright owner. For the purposes of this definition,
49 | "submitted" means any form of electronic, verbal, or written communication sent
50 | to the Licensor or its representatives, including but not limited to
51 | communication on electronic mailing lists, source code control systems, and
52 | issue tracking systems that are managed by, or on behalf of, the Licensor for
53 | the purpose of discussing and improving the Work, but excluding communication
54 | that is conspicuously marked or otherwise designated in writing by the copyright
55 | owner as "Not a Contribution."
56 |
57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58 | of whom a Contribution has been received by Licensor and subsequently
59 | incorporated within the Work.
60 |
61 | 2. Grant of Copyright License.
62 |
63 | Subject to the terms and conditions of this License, each Contributor hereby
64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65 | irrevocable copyright license to reproduce, prepare Derivative Works of,
66 | publicly display, publicly perform, sublicense, and distribute the Work and such
67 | Derivative Works in Source or Object form.
68 |
69 | 3. Grant of Patent License.
70 |
71 | Subject to the terms and conditions of this License, each Contributor hereby
72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73 | irrevocable (except as stated in this section) patent license to make, have
74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75 | such license applies only to those patent claims licensable by such Contributor
76 | that are necessarily infringed by their Contribution(s) alone or by combination
77 | of their Contribution(s) with the Work to which such Contribution(s) was
78 | submitted. If You institute patent litigation against any entity (including a
79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80 | Contribution incorporated within the Work constitutes direct or contributory
81 | patent infringement, then any patent licenses granted to You under this License
82 | for that Work shall terminate as of the date such litigation is filed.
83 |
84 | 4. Redistribution.
85 |
86 | You may reproduce and distribute copies of the Work or Derivative Works thereof
87 | in any medium, with or without modifications, and in Source or Object form,
88 | provided that You meet the following conditions:
89 |
90 | You must give any other recipients of the Work or Derivative Works a copy of
91 | this License; and
92 | You must cause any modified files to carry prominent notices stating that You
93 | changed the files; and
94 | You must retain, in the Source form of any Derivative Works that You distribute,
95 | all copyright, patent, trademark, and attribution notices from the Source form
96 | of the Work, excluding those notices that do not pertain to any part of the
97 | Derivative Works; and
98 | If the Work includes a "NOTICE" text file as part of its distribution, then any
99 | Derivative Works that You distribute must include a readable copy of the
100 | attribution notices contained within such NOTICE file, excluding those notices
101 | that do not pertain to any part of the Derivative Works, in at least one of the
102 | following places: within a NOTICE text file distributed as part of the
103 | Derivative Works; within the Source form or documentation, if provided along
104 | with the Derivative Works; or, within a display generated by the Derivative
105 | Works, if and wherever such third-party notices normally appear. The contents of
106 | the NOTICE file are for informational purposes only and do not modify the
107 | License. You may add Your own attribution notices within Derivative Works that
108 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
109 | provided that such additional attribution notices cannot be construed as
110 | modifying the License.
111 | You may add Your own copyright statement to Your modifications and may provide
112 | additional or different license terms and conditions for use, reproduction, or
113 | distribution of Your modifications, or for any such Derivative Works as a whole,
114 | provided Your use, reproduction, and distribution of the Work otherwise complies
115 | with the conditions stated in this License.
116 |
117 | 5. Submission of Contributions.
118 |
119 | Unless You explicitly state otherwise, any Contribution intentionally submitted
120 | for inclusion in the Work by You to the Licensor shall be under the terms and
121 | conditions of this License, without any additional terms or conditions.
122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
123 | any separate license agreement you may have executed with Licensor regarding
124 | such Contributions.
125 |
126 | 6. Trademarks.
127 |
128 | This License does not grant permission to use the trade names, trademarks,
129 | service marks, or product names of the Licensor, except as required for
130 | reasonable and customary use in describing the origin of the Work and
131 | reproducing the content of the NOTICE file.
132 |
133 | 7. Disclaimer of Warranty.
134 |
135 | Unless required by applicable law or agreed to in writing, Licensor provides the
136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138 | including, without limitation, any warranties or conditions of TITLE,
139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140 | solely responsible for determining the appropriateness of using or
141 | redistributing the Work and assume any risks associated with Your exercise of
142 | permissions under this License.
143 |
144 | 8. Limitation of Liability.
145 |
146 | In no event and under no legal theory, whether in tort (including negligence),
147 | contract, or otherwise, unless required by applicable law (such as deliberate
148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
149 | liable to You for damages, including any direct, indirect, special, incidental,
150 | or consequential damages of any character arising as a result of this License or
151 | out of the use or inability to use the Work (including but not limited to
152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153 | any and all other commercial damages or losses), even if such Contributor has
154 | been advised of the possibility of such damages.
155 |
156 | 9. Accepting Warranty or Additional Liability.
157 |
158 | While redistributing the Work or Derivative Works thereof, You may choose to
159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160 | other liability obligations and/or rights consistent with this License. However,
161 | in accepting such obligations, You may act only on Your own behalf and on Your
162 | sole responsibility, not on behalf of any other Contributor, and only if You
163 | agree to indemnify, defend, and hold each Contributor harmless for any liability
164 | incurred by, or claims asserted against, such Contributor by reason of your
165 | accepting any such warranty or additional liability.
166 |
167 | END OF TERMS AND CONDITIONS
168 |
169 | APPENDIX: How to apply the Apache License to your work
170 |
171 | To apply the Apache License to your work, attach the following boilerplate
172 | notice, with the fields enclosed by brackets "[]" replaced with your own
173 | identifying information. (Don't include the brackets!) The text should be
174 | enclosed in the appropriate comment syntax for the file format. We also
175 | recommend that a file or class name and description of purpose be included on
176 | the same "printed page" as the copyright notice for easier identification within
177 | third-party archives.
178 |
179 | Copyright [yyyy] [name of copyright owner]
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | http://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | #region 文档说明
2 | /* ****************************************************************************************************
3 | * 文档作者:无闻
4 | * 创建日期:2013 年 10 月 16 日
5 | * 文档用途:七牛云盘程序主入口,用于必要文件检查等操作
6 | * -----------------------------------------------------------------------------------------------------
7 | * 修改记录:
8 | * -----------------------------------------------------------------------------------------------------
9 | * 参考文献:
10 | * *****************************************************************************************************/
11 | #endregion
12 |
13 | #region 命名空间引用
14 | using System;
15 | using System.IO;
16 | using System.Windows.Forms;
17 | using System.Diagnostics;
18 |
19 | using CharmCommonMethod;
20 | using CharmControlLibrary;
21 |
22 | #endregion
23 |
24 | namespace QiNiuDrive
25 | {
26 | // 程序主入口
27 | static class Program
28 | {
29 | #region 常量
30 | private const string APP_NAME = "七牛云盘"; // 应用程序名称
31 | #endregion
32 |
33 | #region 方法
34 | // 应用程序的主入口点。
35 | [STAThread]
36 | static void Main()
37 | {
38 | if (!Directory.Exists("Data"))
39 | Directory.CreateDirectory("Data");
40 |
41 | // 检查相同进程是否正在运行
42 | if (CheckSameProcess())
43 | return;
44 |
45 | Application.EnableVisualStyles();
46 | Application.SetCompatibleTextRenderingDefault(false);
47 | Application.Run(new FrmMain());
48 | }
49 |
50 | // 检查相同进程是否正在运行
51 | // 返回:true=有相同进程; false=没有相同进程
52 | public static bool CheckSameProcess()
53 | {
54 | // 判断是否存在禁止检查标识
55 | if (File.Exists("Data\\UAC.SIGN"))
56 | {
57 | File.Delete("Data\\UAC.SIGN");
58 | // 创建UAC模式标识
59 | FileStream fs = File.Create("Data\\UACMODE.SIGN");
60 | fs.Dispose();
61 | }
62 | else
63 | {
64 | // 获取同名进程数量
65 | Process[] procs = Process.GetProcessesByName(APP_NAME);
66 | // 判断返回的进程数组长度,如果数组长度大于1则说明相同程序已经启动
67 | if (procs.Length <= 1) return false;
68 |
69 | // 判断程序启动模式
70 | if (File.Exists("Data\\UACMODE.SIGN"))
71 | {
72 | // 相同进程采用 UAC 模式运行
73 | CharmMessageBox msgbox = new CharmMessageBox();
74 | msgbox.Show("尊敬的用户您好:\n" +
75 | "欢迎您使用 " + APP_NAME + "!\n\n" +
76 | "由于您的系统开启了 UAC 安全控制," +
77 | "程序无法进行相关操作,因此您需要手动恢复已打开的程序窗口!\n\n" +
78 | "感谢您对 " + APP_NAME + " 的支持与理解!",
79 | "温馨提示");
80 | }
81 | else
82 | {
83 | Process currentProc = Process.GetCurrentProcess();
84 | // 对比进程 ID,排除非自身进程,找到已经启动的主程序,并显示主窗口
85 | foreach (Process proc in procs)
86 | {
87 | if (proc.Id == currentProc.Id) continue;
88 | IntPtr hwnd = APIOperation.GetWindowHandle(APP_NAME);
89 | APIOperation.ShowWindowForeground(hwnd);
90 | }
91 | }
92 | // 检测到正在运行的相同进程
93 | return true;
94 | }
95 |
96 | // 没有检测到正在运行的相同进程
97 | return false;
98 | }
99 | #endregion
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的常规信息通过以下
6 | // 特性集控制。更改这些特性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("QiNiuDrive")]
9 | [assembly: AssemblyDescription("七牛云盘是基于七牛开放 API 构建的第三方同步程序")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("无闻")]
12 | [assembly: AssemblyProduct("QiNiuDrive")]
13 | [assembly: AssemblyCopyright("Copyright © 2013 无闻")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 使此程序集中的类型
18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
19 | // 则将该类型上的 ComVisible 特性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("fbf10096-0bf0-496b-92b9-fe6a1f132a45")]
24 |
25 | // 程序集的版本信息由下面四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 生成号
30 | // 修订号
31 | //
32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
33 | // 方法是按如下所示使用“*”:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("0.1.3.1021")]
37 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.33440
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace QiNiuDrive.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("QiNiuDrive.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
65 | ///
66 | internal static System.Drawing.Icon icon {
67 | get {
68 | object obj = ResourceManager.GetObject("icon", resourceCulture);
69 | return ((System.Drawing.Icon)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Bitmap.
75 | ///
76 | internal static System.Drawing.Bitmap logo {
77 | get {
78 | object obj = ResourceManager.GetObject("logo", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Bitmap.
85 | ///
86 | internal static System.Drawing.Bitmap qiniu_logo {
87 | get {
88 | object obj = ResourceManager.GetObject("qiniu_logo", resourceCulture);
89 | return ((System.Drawing.Bitmap)(obj));
90 | }
91 | }
92 |
93 | ///
94 | /// Looks up a localized resource of type System.Drawing.Bitmap.
95 | ///
96 | internal static System.Drawing.Bitmap tab_button_setting_chosen {
97 | get {
98 | object obj = ResourceManager.GetObject("tab_button_setting_chosen", resourceCulture);
99 | return ((System.Drawing.Bitmap)(obj));
100 | }
101 | }
102 |
103 | ///
104 | /// Looks up a localized resource of type System.Drawing.Bitmap.
105 | ///
106 | internal static System.Drawing.Bitmap unknown {
107 | get {
108 | object obj = ResourceManager.GetObject("unknown", resourceCulture);
109 | return ((System.Drawing.Bitmap)(obj));
110 | }
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\Icons\icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\Resources\Icons\logo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
128 | ..\Resources\FrmMain\qiniu_logo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
131 | ..\Resources\FrmMain\tab_button_setting_chosen.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
132 |
133 |
134 | ..\Resources\FrmMain\unknown.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
135 |
136 |
--------------------------------------------------------------------------------
/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.33440
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace QiNiuDrive.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/QiNiuDrive.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {EC83ADAF-F396-4385-A1E2-A1CFA1BD336F}
8 | WinExe
9 | Properties
10 | QiNiuDrive
11 | 七牛云盘
12 | v4.0
13 | 512
14 |
15 | publish\
16 | true
17 | Disk
18 | false
19 | Foreground
20 | 7
21 | Days
22 | false
23 | false
24 | true
25 | 0
26 | 1.0.0.%2a
27 | false
28 | false
29 | true
30 |
31 |
32 | x86
33 | true
34 | full
35 | false
36 | ..\Applications\七牛云盘\
37 | DEBUG;TRACE
38 | prompt
39 | 4
40 |
41 |
42 | x86
43 | pdbonly
44 | true
45 | ..\Applications\七牛云盘\
46 | TRACE
47 | prompt
48 | 4
49 |
50 |
51 | Resources\Icons\icon.ico
52 |
53 |
54 |
55 | ..\Libraries\CharmCommonMethod.dll
56 |
57 |
58 | ..\Libraries\CharmControlLibrary.dll
59 |
60 |
61 | False
62 | ..\Libraries\Newtonsoft.Json.dll
63 |
64 |
65 | ..\Libraries\Qiniu.dll
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Form
80 |
81 |
82 | FrmFilterTester.cs
83 |
84 |
85 | Form
86 |
87 |
88 | FrmMain.cs
89 |
90 |
91 |
92 |
93 | FrmFilterTester.cs
94 |
95 |
96 | FrmMain.cs
97 |
98 |
99 | ResXFileCodeGenerator
100 | Resources.Designer.cs
101 | Designer
102 |
103 |
104 | True
105 | Resources.resx
106 | True
107 |
108 |
109 |
110 | SettingsSingleFileGenerator
111 | Settings.Designer.cs
112 |
113 |
114 | True
115 | Settings.settings
116 | True
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | False
137 | Microsoft .NET Framework 4 %28x86 and x64%29
138 | true
139 |
140 |
141 | False
142 | .NET Framework 3.5 SP1 Client Profile
143 | false
144 |
145 |
146 | False
147 | .NET Framework 3.5 SP1
148 | false
149 |
150 |
151 | False
152 | Windows Installer 4.5
153 | true
154 |
155 |
156 |
157 |
164 |
--------------------------------------------------------------------------------
/QiNiuDrive.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | No
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 七牛云盘
2 | ==========
3 | []()
4 |
5 | 七牛云盘是基于七牛开放 API 构建的第三方同步程序。
6 |
7 | ## 软件功能(v0.1.5)
8 |
9 | - [更新日志](https://github.com/Unknwon/qiniudrive/wiki/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97)
10 | - 自定义同步目录并进行单向上传同步。
11 | - 支持对每个设备单独设置前缀,避免覆盖及帮助识别。
12 | - 支持设置 `Config/filter.txt` 进行文件过滤。
13 | - `*/.git/*` 表示包含过滤。
14 | - `*.txt` 表示后缀过滤。
15 | - `look*` 表示前缀过滤。
16 | - `ok/fine.txt` 表示相对于同步目录的相对路径过滤。
17 |
18 | ## 注意事项
19 |
20 | - 当前版本仅支持文件或目录的 **重命名** 事件捕捉,如果文件或目录发生 **移动** 操作,则会先删除,再上传。
21 | - 书写文件过滤规则时,目录分割符均为 `/`。
22 | - 默认忽略所有隐藏文件、以 `.tmp` 结尾的文件。
23 |
24 | ## 已知问题
25 |
26 | - 如果您将旧的文件覆盖同步目录中的同名文件,会重新从云端下载旧的文件,这是由于通过只判断最后修改时间导致的。
27 |
28 | ## 主要原理
29 |
30 | - 同步周期:为节省 API 调用次数,软件根据用户指定周期(默认为 60 秒)进行文件差异判断,因此如果您在不同设备间进行同步,务必人为确保完成同步后,再修改其它设备上的文件。
31 | - 差异判断:通过文件最后修改时间完成差异判断,因此如果您通过其它手段非正常你不能 修改文件时间后可能会导致同步结果无法预料。
32 | - 文件删除:每次同步完成后会将本地文件建立缓存列表并保存至 `Data/file_cache_list`。因此,您对该文件的任何非法改动都可能将导致您的数据意外丢失!
33 | - 云端删除:判断是需要上传还是其它设备将文件删除,也是依靠本地缓存列表判断的。如果已存在缓存列表中,则说明该文件已经与云端完成同步,此时发现云端文件被删除,既表示是从其它设备进行删除操作;反之,则表示未进行过同步,需要上传。
34 | - 文件重命名:为节省 API 调用次数,在软件运行时采用事件捕捉机制,在同步时使用批量移动操作统一重命名。如果您在未启动软件时重命名文件或目录,则会导致软件进行先删除,再上传操作。
35 |
36 | ## 界面预览
37 |
38 | 
39 |
--------------------------------------------------------------------------------
/Resources/FrmMain/qiniu_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unknwon/qiniudrive/bc3d53de13c1628b37932ea06575f4c22ef92a3c/Resources/FrmMain/qiniu_logo.png
--------------------------------------------------------------------------------
/Resources/FrmMain/tab_button_setting_chosen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unknwon/qiniudrive/bc3d53de13c1628b37932ea06575f4c22ef92a3c/Resources/FrmMain/tab_button_setting_chosen.png
--------------------------------------------------------------------------------
/Resources/FrmMain/unknown.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unknwon/qiniudrive/bc3d53de13c1628b37932ea06575f4c22ef92a3c/Resources/FrmMain/unknown.jpg
--------------------------------------------------------------------------------
/Resources/Icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unknwon/qiniudrive/bc3d53de13c1628b37932ea06575f4c22ef92a3c/Resources/Icons/icon.ico
--------------------------------------------------------------------------------
/Resources/Icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unknwon/qiniudrive/bc3d53de13c1628b37932ea06575f4c22ef92a3c/Resources/Icons/logo.png
--------------------------------------------------------------------------------
/VERSION.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 201312030,
3 | "download_url": "https://github.com/Unknwon/qiniudrive"
4 | }
--------------------------------------------------------------------------------
/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/imgs/SyncSetting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unknwon/qiniudrive/bc3d53de13c1628b37932ea06575f4c22ef92a3c/imgs/SyncSetting.png
--------------------------------------------------------------------------------
/libs.rar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unknwon/qiniudrive/bc3d53de13c1628b37932ea06575f4c22ef92a3c/libs.rar
--------------------------------------------------------------------------------