C# - VS2019WinFrm桌面应用程序FtpClient实现


前言

本篇主要记录:VS2019 WinFrm桌面应用程序实现简单的FtpClient,包含Ftp文件查看、上传和下载等功能。

准备工作

搭建WinFrm前台界面

添加必要的控件,这里主要应用到GroupBox、Label、TextBox和Button,如下图

核心代码

构造FtpHelper类

代码如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.IO;
  4 using System.Linq;
  5 using System.Net;
  6 using System.Text;
  7 using System.Threading.Tasks;
  8 
  9 namespace FtpClientTest
 10 {
 11     public class FtpHelper
 12     {
 13         // FTP连接地址
 14         string ftpServerIP;
 15         // 指定FTP连接成功后的当前目录, 如果不指定即默认为根目录
 16         string ftpRemotePath;
 17         // 用户名
 18         string ftpUserID;
 19         // 密码
 20         string ftpPassword;
 21         // 通用处理路径
 22         string ftpURI;
 23 
 24         /// <summary>
 25         /// 连接FTP
 26         /// </summary>
 27         /// <param name="FtpServerIP">FTP连接地址</param>
 28         /// <param name="FtpRemotePath">指定FTP连接成功后的当前目录, 如果不指定即默认为根目录</param>
 29         /// <param name="FtpUserID">用户名</param>
 30         /// <param name="FtpPassword">密码</param>
 31         public FtpHelper(string FtpServerIP, string FtpRemotePath, string FtpUserID, string FtpPassword)
 32         {
 33             ftpServerIP = FtpServerIP;
 34             ftpRemotePath = FtpRemotePath;
 35             ftpUserID = FtpUserID;
 36             ftpPassword = FtpPassword;
 37             ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/";
 38         }
 39 
 40         /// <summary>
 41         /// 上传
 42         /// </summary>
 43         /// <param name="filename">本地文件全路径</param>
 44         public void Upload(string filename)
 45         {
 46             FileInfo fileInf = new FileInfo(filename);
 47             string uri = ftpURI + fileInf.Name;
 48             FtpWebRequest reqFTP;
 49 
 50             reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
 51             reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
 52             reqFTP.KeepAlive = false;
 53             reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
 54             reqFTP.UseBinary = true;
 55             reqFTP.UsePassive = false;
 56             reqFTP.ContentLength = fileInf.Length;
 57             int buffLength = 2048;
 58             byte[] buff = new byte[buffLength];
 59             int contentLen;
 60             FileStream fs = fileInf.OpenRead();
 61             try
 62             {
 63                 Stream strm = reqFTP.GetRequestStream();
 64                 contentLen = fs.Read(buff, 0, buffLength);
 65                 while (contentLen != 0)
 66                 {
 67                     strm.Write(buff, 0, contentLen);
 68                     contentLen = fs.Read(buff, 0, buffLength);
 69                 }
 70                 strm.Close();
 71                 fs.Close();
 72             }
 73             catch (Exception ex)
 74             {
 75                 throw new Exception("Ftphelper Upload Error --> " + ex.Message);
 76             }
 77         }
 78 
 79         /// <summary>
 80         /// 下载
 81         /// </summary>
 82         /// <param name="filePath">文件保存本地路径</param>
 83         /// <param name="fileName">Ftp上文件的路径</param>
 84         public void Download(string filePath, string fileName)
 85         {
 86             FtpWebRequest reqFTP;
 87             try
 88             {
 89                 FileStream outputStream = new FileStream(filePath + "\\" + fileName, FileMode.Create);
 90 
 91                 reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + fileName));
 92                 reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
 93                 reqFTP.UseBinary = true;
 94                 reqFTP.UsePassive = false;
 95                 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
 96                 FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
 97                 Stream ftpStream = response.GetResponseStream();
 98                 long cl = response.ContentLength;
 99                 int bufferSize = 2048;
100                 int readCount;
101                 byte[] buffer = new byte[bufferSize];
102 
103                 readCount = ftpStream.Read(buffer, 0, bufferSize);
104                 while (readCount > 0)
105                 {
106                     outputStream.Write(buffer, 0, readCount);
107                     readCount = ftpStream.Read(buffer, 0, bufferSize);
108                 }
109 
110                 ftpStream.Close();
111                 outputStream.Close();
112                 response.Close();
113             }
114             catch (Exception ex)
115             {
116                 throw new Exception("FtpHelper Download Error --> " + ex.Message);
117             }
118         }
119 
120         /// <summary>
121         /// 删除文件
122         /// </summary>
123         /// <param name="fileName">Ftp上文件的名称</param>
124         public void Delete(string fileName)
125         {
126             try
127             {
128                 string uri = ftpURI + fileName;
129                 FtpWebRequest reqFTP;
130                 reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
131 
132                 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
133                 reqFTP.KeepAlive = false;
134                 reqFTP.Method = WebRequestMethods.Ftp.DeleteFile;
135                 reqFTP.UsePassive = false;
136 
137                 string result = String.Empty;
138                 FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
139                 long size = response.ContentLength;
140                 Stream datastream = response.GetResponseStream();
141                 StreamReader sr = new StreamReader(datastream);
142                 result = sr.ReadToEnd();
143                 sr.Close();
144                 datastream.Close();
145                 response.Close();
146             }
147             catch (Exception ex)
148             {
149                 throw new Exception("FtpHelper Delete Error --> " + ex.Message + "  文件名:" + fileName);
150             }
151         }
152 
153         /// <summary>
154         /// 删除文件夹
155         /// </summary>
156         /// <param name="folderName">Ftp上文件夹的名称</param>
157         public void RemoveDirectory(string folderName)
158         {
159             try
160             {
161                 string uri = ftpURI + folderName;
162                 FtpWebRequest reqFTP;
163                 reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
164 
165                 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
166                 reqFTP.KeepAlive = false;
167                 reqFTP.Method = WebRequestMethods.Ftp.RemoveDirectory;
168                 reqFTP.UsePassive = false;
169 
170                 string result = String.Empty;
171                 FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
172                 long size = response.ContentLength;
173                 Stream datastream = response.GetResponseStream();
174                 StreamReader sr = new StreamReader(datastream);
175                 result = sr.ReadToEnd();
176                 sr.Close();
177                 datastream.Close();
178                 response.Close();
179             }
180             catch (Exception ex)
181             {
182                 throw new Exception("FtpHelper Delete Error --> " + ex.Message + "  文件名:" + folderName);
183             }
184         }
185 
186         /// <summary>
187         /// 获取当前目录下明细(包含文件和文件夹)
188         /// </summary>
189         /// <returns></returns>
190         public string[] GetFilesDetailList()
191         {
192             string[] downloadFiles;
193             try
194             {
195                 StringBuilder result = new StringBuilder();
196                 FtpWebRequest ftp;
197                 ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI));
198                 ftp.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
199                 ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
200                 ftp.UsePassive = false;
201                 WebResponse response = ftp.GetResponse();
202                 StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default);
203 
204                 //while (reader.Read() > 0)
205                 //{
206 
207                 //}
208                 string line = reader.ReadLine();
209                 //line = reader.ReadLine();
210                 //line = reader.ReadLine();
211 
212                 while (line != null)
213                 {
214                     result.Append(line);
215                     result.Append("\n");
216                     line = reader.ReadLine();
217                 }
218                 result.Remove(result.ToString().LastIndexOf("\n"), 1);
219                 reader.Close();
220                 response.Close();
221                 return result.ToString().Split('\n');
222             }
223             catch (Exception ex)
224             {
225                 downloadFiles = null;
226                 throw new Exception("FtpHelper  Error --> " + ex.Message);
227             }
228         }
229 
230         /// <summary>
231         /// 获取当前目录下文件列表(仅文件)
232         /// </summary>
233         /// <returns></returns>
234         public string[] GetFileList(string mask)
235         {
236             string[] downloadFiles;
237             StringBuilder result = new StringBuilder();
238             FtpWebRequest reqFTP;
239             try
240             {
241                 reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI));
242                 reqFTP.UseBinary = true;
243                 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
244                 reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
245                 reqFTP.UsePassive = false;
246                 WebResponse response = reqFTP.GetResponse();
247                 StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default);
248 
249                 string line = reader.ReadLine();
250                 while (line != null)
251                 {
252                     if (mask.Trim() != string.Empty && mask.Trim() != "*.*")
253                     {
254 
255                         string mask_ = mask.Substring(0, mask.IndexOf("*"));
256                         if (line.Substring(0, mask_.Length) == mask_)
257                         {
258                             result.Append(line);
259                             result.Append("\n");
260                         }
261                     }
262                     else
263                     {
264                         result.Append(line);
265                         result.Append("\n");
266                     }
267                     line = reader.ReadLine();
268                 }
269                 result.Remove(result.ToString().LastIndexOf('\n'), 1);
270                 reader.Close();
271                 response.Close();
272                 return result.ToString().Split('\n');
273             }
274             catch (Exception ex)
275             {
276                 downloadFiles = null;
277                 if (ex.Message.Trim() != "远程服务器返回错误: (550) 文件不可用(例如,未找到文件,无法访问文件)。")
278                 {
279                     throw new Exception("FtpHelper GetFileList Error --> " + ex.Message.ToString());
280                 }
281                 return downloadFiles;
282             }
283         }
284 
285         /// <summary>
286         /// 获取当前目录下所有的文件夹列表(仅文件夹)
287         /// </summary>
288         /// <returns></returns>
289         public string[] GetDirectoryList()
290         {
291             string[] drectory = GetFilesDetailList();
292             string m = string.Empty;
293             foreach (string str in drectory)
294             {
295                 int dirPos = str.IndexOf("<DIR>");
296                 if (dirPos > 0)
297                 {
298                     /*判断 Windows 风格*/
299                     m += str.Substring(dirPos + 5).Trim() + "\n";
300                 }
301                 else if (str.Trim().Substring(0, 1).ToUpper() == "D")
302                 {
303                     /*判断 Unix 风格*/
304                     string dir = str.Substring(54).Trim();
305                     if (dir != "." && dir != "..")
306                     {
307                         m += dir + "\n";
308                     }
309                 }
310             }
311 
312             char[] n = new char[] { '\n' };
313             return m.Split(n);
314         }
315 
316         /// <summary>
317         /// 判断当前目录下指定的子目录是否存在
318         /// </summary>
319         /// <param name="RemoteDirectoryName">指定的目录名</param>
320         public bool DirectoryExist(string RemoteDirectoryName)
321         {
322             string[] dirList = GetDirectoryList();
323             foreach (string str in dirList)
324             {
325                 if (str.Trim() == RemoteDirectoryName.Trim())
326                 {
327                     return true;
328                 }
329             }
330             return false;
331         }
332 
333         /// <summary>
334         /// 判断当前目录下指定的文件是否存在
335         /// </summary>
336         /// <param name="RemoteFileName">远程文件名</param>
337         public bool FileExist(string RemoteFileName)
338         {
339             string[] fileList = GetFileList("*.*");
340             foreach (string str in fileList)
341             {
342                 if (str.Trim() == RemoteFileName.Trim())
343                 {
344                     return true;
345                 }
346             }
347             return false;
348         }
349 
350         /// <summary>
351         /// 创建文件夹
352         /// </summary>
353         /// <param name="dirName"></param>
354         public void MakeDir(string dirName)
355         {
356             FtpWebRequest reqFTP;
357             try
358             {
359                 // dirName = name of the directory to create.
360                 reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + dirName));
361                 reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
362                 reqFTP.UseBinary = true;
363                 reqFTP.UsePassive = false;
364                 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
365                 FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
366                 Stream ftpStream = response.GetResponseStream();
367 
368                 ftpStream.Close();
369                 response.Close();
370             }
371             catch (Exception ex)
372             {
373                 throw new Exception("FtpHelper MakeDir Error --> " + ex.Message);
374             }
375         }
376 
377         /// <summary>
378         /// 获取指定文件大小
379         /// </summary>
380         /// <param name="filename"></param>
381         /// <returns></returns>
382         public long GetFileSize(string filename)
383         {
384             FtpWebRequest reqFTP;
385             long fileSize = 0;
386             try
387             {
388                 reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + filename));
389                 reqFTP.Method = WebRequestMethods.Ftp.GetFileSize;
390                 reqFTP.UseBinary = true;
391                 reqFTP.UsePassive = false;
392                 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
393                 FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
394                 Stream ftpStream = response.GetResponseStream();
395                 fileSize = response.ContentLength;
396 
397                 ftpStream.Close();
398                 response.Close();
399             }
400             catch (Exception ex)
401             {
402                 throw new Exception("FtpHelper GetFileSize Error --> " + ex.Message);
403             }
404             return fileSize;
405         }
406 
407         /// <summary>
408         /// 改名
409         /// </summary>
410         /// <param name="currentFilename"></param>
411         /// <param name="newFilename"></param>
412         public void ReName(string currentFilename, string newFilename)
413         {
414             FtpWebRequest reqFTP;
415             try
416             {
417                 reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + currentFilename));
418                 reqFTP.Method = WebRequestMethods.Ftp.Rename;
419                 reqFTP.RenameTo = newFilename;
420                 reqFTP.UseBinary = true;
421                 reqFTP.UsePassive = false;
422                 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
423                 FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
424                 Stream ftpStream = response.GetResponseStream();
425 
426                 ftpStream.Close();
427                 response.Close();
428             }
429             catch (Exception ex)
430             {
431                 throw new Exception("FtpHelper ReName Error --> " + ex.Message);
432             }
433         }
434 
435         /// <summary>
436         /// 移动文件
437         /// </summary>
438         /// <param name="currentFilename"></param>
439         /// <param name="newFilename"></param>
440         public void MovieFile(string currentFilename, string newDirectory)
441         {
442             ReName(currentFilename, newDirectory);
443         }
444 
445         /// <summary>
446         /// 切换当前目录
447         /// </summary>
448         /// <param name="DirectoryName"></param>
449         /// <param name="IsRoot">true 绝对路径   false 相对路径</param>
450         public void GotoDirectory(string DirectoryName, bool IsRoot)
451         {
452             if (IsRoot)
453             {
454                 ftpRemotePath = DirectoryName;
455             }
456             else
457             {
458                 ftpRemotePath += DirectoryName + "/";
459             }
460             ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/";
461         }
462 
463         /// <summary>
464         /// 删除订单目录
465         /// </summary>
466         /// <param name="ftpServerIP">FTP 主机地址</param>
467         /// <param name="folderToDelete">FTP 用户名</param>
468         /// <param name="ftpUserID">FTP 用户名</param>
469         /// <param name="ftpPassword">FTP 密码</param>
470         public static void DeleteOrderDirectory(string ftpServerIP, string folderToDelete, string ftpUserID, string ftpPassword)
471         {
472             try
473             {
474                 if (!string.IsNullOrEmpty(ftpServerIP) && !string.IsNullOrEmpty(folderToDelete) && !string.IsNullOrEmpty(ftpUserID) && !string.IsNullOrEmpty(ftpPassword))
475                 {
476                     FtpHelper fw = new FtpHelper(ftpServerIP, folderToDelete, ftpUserID, ftpPassword);
477                     //进入订单目录
478                     fw.GotoDirectory(folderToDelete, true);
479                     //获取规格目录
480                     string[] folders = fw.GetDirectoryList();
481                     foreach (string folder in folders)
482                     {
483                         if (!string.IsNullOrEmpty(folder) || folder != "")
484                         {
485                             //进入订单目录
486                             string subFolder = folderToDelete + "/" + folder;
487                             fw.GotoDirectory(subFolder, true);
488                             //获取文件列表
489                             string[] files = fw.GetFileList("*.*");
490                             if (files != null)
491                             {
492                                 //删除文件
493                                 foreach (string file in files)
494                                 {
495                                     fw.Delete(file);
496                                 }
497                             }
498                             //删除冲印规格文件夹
499                             fw.GotoDirectory(folderToDelete, true);
500                             fw.RemoveDirectory(folder);
501                         }
502                     }
503 
504                     //删除订单文件夹
505                     string parentFolder = folderToDelete.Remove(folderToDelete.LastIndexOf('/'));
506                     string orderFolder = folderToDelete.Substring(folderToDelete.LastIndexOf('/') + 1);
507                     fw.GotoDirectory(parentFolder, true);
508                     fw.RemoveDirectory(orderFolder);
509                 }
510                 else
511                 {
512                     throw new Exception("FTP 及路径不能为空!");
513                 }
514             }
515             catch (Exception ex)
516             {
517                 throw new Exception("删除订单时发生错误,错误信息为:" + ex.Message);
518             }
519         }
520     }
521 }
View Code

配合控件重写Ftp方法

代码如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.IO;
  7 using System.Linq;
  8 using System.Net;
  9 using System.Text;
 10 using System.Threading.Tasks;
 11 using System.Windows.Forms;
 12 
 13 namespace FtpClientTest
 14 {
 15     public partial class FrmMain : Form
 16     {
 17         public FrmMain()
 18         {
 19             InitializeComponent();
 20         }
 21 
 22         // 定义全局变量 ftp参数
 23         private FtpHelper ftpHelper;
 24         private string ipAddr;
 25         private string port;
 26         private string path;
 27         private string userName;
 28         private string password;
 29 
 30         /// <summary>
 31         /// 方法:Ftp初始化
 32         /// </summary>
 33         private void FtpInit()
 34         {
 35             ipAddr = this.txtHost.Text;
 36             port = this.txtHost.Text;
 37             path = this.txtCurrentDir.Text;
 38             userName = this.txtUserName.Text;
 39             password = this.txtPassCode.Text;
 40             ftpHelper = new FtpHelper(ipAddr, path, userName, password);
 41         }
 42  
 43         /// <summary>
 44         /// 事件:文件下载
 45         /// </summary>
 46         /// <param name="sender"></param>
 47         /// <param name="e"></param>
 48         private void btnDownLoad_Click(object sender, EventArgs e)
 49         {
 50             try
 51             {
 52                 ftpHelper.Download(@"D:\", this.txtFilePath.Text.Trim());
 53                 MessageBox.Show("下载成功!", "提示");
 54             }
 55             catch (Exception ex)
 56             {
 57                 MessageBox.Show("下载失败!错误信息[" + ex.Message + "]", "警告");
 58             }
 59 
 60         }
 61         /// <summary>
 62         /// 方法:获取Ftp当前目录下的文件
 63         /// </summary>
 64         private void GetFileList()
 65         {
 66             string[] str = ftpHelper.GetFilesDetailList();
 67             tbFileLists.Text = "";
 68             for (int i = 0; i < str.Length - 1; i++)
 69             {
 70                 if (tbFileLists.Text.ToString() == "")
 71                 {
 72                     tbFileLists.Text = str[i];
 73                 }
 74                 else
 75                 {
 76                     tbFileLists.Text = tbFileLists.Text + "\r\n" + str[i];
 77                 }
 78             }
 79         }
 80 
 81         /// <summary>
 82         /// 事件:连接Ftp,获取文件列表
 83         /// </summary>
 84         /// <param name="sender"></param>
 85         /// <param name="e"></param>
 86         private void btnConnect_Click(object sender, EventArgs e)
 87         {
 88             try
 89             {
 90                 FtpInit();
 91                 this.btnDownLoad.Enabled = true;
 92                 this.btnUpload.Enabled = true;
 93                 this.btnOpen.Enabled = true;
 94                 GetFileList();
 95             }
 96             catch (Exception ex)
 97             {
 98                 this.btnDownLoad.Enabled = false;
 99                 this.btnUpload.Enabled = false;
100                 this.btnOpen.Enabled = false;
101                 MessageBox.Show("连接失败!错误信息[" + ex.Message + "]", "警告");
102             }
103         }
104 
105         /// <summary>
106         /// 事件:上传文件
107         /// </summary>
108         /// <param name="sender"></param>
109         /// <param name="e"></param>
110         private void btnUpload_Click(object sender, EventArgs e)
111         {
112             try
113             {
114                 ftpHelper.Upload(this.txtLocalFilePath.Text.Trim());
115                 MessageBox.Show("上传成功!", "提示");
116             }
117             catch (Exception ex)
118             {
119                 MessageBox.Show("上传失败!错误信息[" + ex.Message + "]", "警告");
120             }
121         }
122 
123         /// <summary>
124         /// 事件:选中需要上传的文件
125         /// </summary>
126         /// <param name="sender"></param>
127         /// <param name="e"></param>
128         private void btnOpen_Click(object sender, EventArgs e)
129         {
130             // 创建 OpenFileDialog 对象
131             OpenFileDialog ofd = new OpenFileDialog();
132             // 设定默认打开文件类型,删选、设定文件显示类型
133             ofd.Filter = "(*.et;*.xls;*.xlsx)|*.et;*.xls;*.xlsx|all|*.*";           
134             // 显示打开文件的窗口
135             ofd.ShowDialog();
136             // 获得选择的文件路径,并赋值给
137             this.txtLocalFilePath.Text = ofd.FileName;
138         }
139     }
140 }
View Code

实现效果

作者:Jeremy.Wu
出处:https://www.cnblogs.com/jeremywucnblog/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

优质内容筛选与推荐>>
1、Java基础精选,你答对了几道?
2、javaredis通用组建
3、flask第十五篇——Response
4、Phoenix-Hbase与SQL
5、CDH集群升级Python3异常问题分析


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn