
cobra
striver
-
个人空间
相册
- 性别:
- 来自:拼吾爱
- 积分:6836
- 帖子:6748
- 注册:
2007-04-09
|
Discuz!NT中远程附件的功能实现[FTP协议]
文/代震军 出处/博客园 大约在去年的12月份,我们开始着手设计和开发这项功能,而该项功能主要是解决类似于一些帖子附件(图片或文件)访问比较频繁,同时附件的体积又比较大,从而造成对主站服务器访问压力过大的问题。而实现了该项功能之后,在一些合作伙伴的站点上使用了一段时间,发现该功能明显的降低了主站服务器的负载,使其可以节省更多的资源(cpu,内存等) 用于处理用户的其它访问请求。 下面就简要介绍一下该功能的一些实现细节, 该项功能所实现的流程如下图所示:  附件: 您所在的用户组无法下载或查看附件 而主要的核心就是采用FTP协议上传附件到远程的服务器上,这样当用户点开网页或进行附件下载时,就会将链接指向远程的FTP服务上(该服务器要支持HTTP协议访问其资源)。而这个类的原型链接如下: http://www.csharphelp.com/archives/archive9.html 本人在其基础上修改了该类在DEBUG模式下上传文件过程中的BUG,同时翻译了其注释内容。大家可在dicuz.common.dll(discuz!nt 2.1以后的版块)的中找到该类(使用Reflector)。 下面是其核心代码(您可在下个开源版本中获取该类的全部代码):  Code
/**//// <summary> /// FTP类 /// </summary> public class FTP { 变量声明#region 变量声明
/**//// <summary> /// 服务器连接地址 /// </summary> public string server;
/**//// <summary> /// 登陆帐号 /// </summary> public string user;
/**//// <summary> /// 登陆口令 /// </summary> public string pass;
/**//// <summary> /// 端口号 /// </summary> public int port;
/**//// <summary> /// 无响应时间(FTP在指定时间内无响应) /// </summary> public int timeout;
/**//// <summary> /// 服务器错误状态信息 /// </summary> public string errormessage;
/**//// <summary> /// 服务器状态返回信息 /// </summary> private string messages;
/**//// <summary> /// 服务器的响应信息 /// </summary> private string responseStr;
/**//// <summary> /// 链接模式(主动或被动,默认为被动) /// </summary> private bool passive_mode;
/**//// <summary> /// 上传或下载信息字节数 /// </summary> private long bytes_total;
/**//// <summary> /// 上传或下载的文件大小 /// </summary> private long file_size;
/**//// <summary> /// 主套接字 /// </summary> private Socket main_sock;
/**//// <summary> /// 要链接的网络地址终结点 /// </summary> private IPEndPoint main_ipEndPoint;
/**//// <summary> /// 侦听套接字 /// </summary> private Socket listening_sock;
/**//// <summary> /// 数据套接字 /// </summary> private Socket data_sock;
/**//// <summary> /// 要链接的网络数据地址终结点 /// </summary> private IPEndPoint data_ipEndPoint;
/**//// <summary> /// 用于上传或下载的文件流对象 /// </summary> private FileStream file;
/**//// <summary> /// 与FTP服务器交互的状态值 /// </summary> private int response;
/**//// <summary> /// 读取并保存当前命令执行后从FTP服务器端返回的数据信息 /// </summary> private string bucket;
#endregion
构造函数#region 构造函数
/**//// <summary> /// 构造函数 /// </summary> public FTP() { server = null; user = null; pass = null; port = 21; passive_mode = true; main_sock = null; main_ipEndPoint = null; listening_sock = null; data_sock = null; data_ipEndPoint = null; file = null; bucket = ""; bytes_total = 0; timeout = 10000; //无响应时间为10秒 messages = ""; errormessage = ""; }
/**//// <summary> /// 构造函数 /// </summary> /// <param name="server">服务器IP或名称</param> /// <param name="user">登陆帐号</param> /// <param name="pass">登陆口令</param> public FTP(string server, string user, string pass) { this.server = server; this.user = user; this.pass = pass; port = 21; passive_mode = true; main_sock = null; main_ipEndPoint = null; listening_sock = null; data_sock = null; data_ipEndPoint = null; file = null; bucket = ""; bytes_total = 0; timeout = 10000; //无响应时间为10秒 messages = ""; errormessage = ""; } /**//// <summary> /// 构造函数 /// </summary> /// <param name="server">服务器IP或名称</param> /// <param name="port">端口号</param> /// <param name="user">登陆帐号</param> /// <param name="pass">登陆口令</param> public FTP(string server, int port, string user, string pass) { this.server = server; this.user = user; this.pass = pass; this.port = port; passive_mode = true; main_sock = null; main_ipEndPoint = null; listening_sock = null; data_sock = null; data_ipEndPoint = null; file = null; bucket = ""; bytes_total = 0; timeout = 10000; //无响应时间为10秒 messages = ""; errormessage = ""; }
/**//// <summary> /// 构造函数 /// </summary> /// <param name="server">服务器IP或名称</param> /// <param name="port">端口号</param> /// <param name="user">登陆帐号</param> /// <param name="pass">登陆口令</param> /// <param name="mode">链接方式</param> public FTP(string server, int port, string user, string pass, int mode) { this.server = server; this.user = user; this.pass = pass; this.port = port; passive_mode = mode <= 1 ? true : false; main_sock = null; main_ipEndPoint = null; listening_sock = null; data_sock = null; data_ipEndPoint = null; file = null; bucket = ""; bytes_total = 0; this.timeout = 10000; //无响应时间为10秒 messages = ""; errormessage = ""; }
/**//// <summary> /// 构造函数 /// </summary> /// <param name="server">服务器IP或名称</param> /// <param name="port">端口号</param> /// <param name="user">登陆帐号</param> /// <param name="pass">登陆口令</param> /// <param name="mode">链接方式</param> /// <param name="timeout">无响应时间(限时),单位:秒 (小于或等于0为不受时间限制)</param> public FTP(string server, int port, string user, string pass, int mode, int timeout_sec) { this.server = server; this.user = user; this.pass = pass; this.port = port; passive_mode = mode <= 1 ? true : false; main_sock = null; main_ipEndPoint = null; listening_sock = null; data_sock = null; data_ipEndPoint = null; file = null; bucket = ""; bytes_total = 0; this.timeout = (timeout_sec <= 0) ? int.MaxValue : (timeout_sec * 1000); //无响应时间 messages = ""; errormessage = ""; }
#endregion
属性#region 属性 /**//// <summary> /// 当前是否已连接 /// </summary> public bool IsConnected { get { if (main_sock != null) return main_sock.Connected; return false; } }
/**//// <summary> /// 当message缓冲区有数据则返回 /// </summary> public bool MessagesAvailable { get { if (messages.Length > 0) return true; return false; } }
/**//// <summary> /// 获取服务器状态返回信息, 并清空messages变量 /// </summary> public string Messages { get { string tmp = messages; messages = ""; return tmp; } } /**//// <summary> /// 最新指令发出后服务器的响应 /// </summary> public string ResponseString { get { return responseStr; } }
/**//// <summary> ///在一次传输中,发送或接收的字节数 /// </summary> public long BytesTotal { get { return bytes_total; } }
/**//// <summary> ///被下载或上传的文件大小,当文件大小无效时为0 /// </summary> public long FileSize { get { return file_size; } }
/**//// <summary> /// 链接模式: /// true 被动模式 [默认] /// false: 主动模式 /// </summary> public bool PassiveMode { get { return passive_mode; } set { passive_mode = value; } }
#endregion
操作#region 操作
/**//// <summary> /// 操作失败 /// </summary> private void Fail() { Disconnect(); errormessage += responseStr; //throw new Exception(responseStr); }
private void SetBinaryMode(bool mode) { if (mode) SendCommand("TYPE I"); else SendCommand("TYPE A");
ReadResponse(); if (response != 200) Fail(); }
private void SendCommand(string command) { Byte[] cmd = Encoding.ASCII.GetBytes((command + "\r\n").ToCharArray());
if (command.Length > 3 && command.Substring(0, 4) == "PASS") { messages = "\rPASS xxx"; } else { messages = "\r" + command; }
try { main_sock.Send(cmd, cmd.Length, 0); } catch (Exception ex) { try { Disconnect(); errormessage += ex.Message; return; } catch { main_sock.Close(); file.Close(); main_sock = null; main_ipEndPoint = null; file = null; } } }
private void FillBucket() { Byte[] bytes = new Byte[512]; long bytesgot; int msecs_passed = 0;
while (main_sock.Available < 1) { System.Threading.Thread.Sleep(50); msecs_passed += 50; //当等待时间到,则断开链接 if (msecs_passed > timeout) { Disconnect(); errormessage += "Timed out waiting on server to respond."; return; } }
while (main_sock.Available > 0) { bytesgot = main_sock.Receive(bytes, 512, 0); bucket += Encoding.ASCII.GetString(bytes, 0, (int)bytesgot); System.Threading.Thread.Sleep(50); } }
private string GetLineFromBucket() { int i; string buf = "";
if ((i = bucket.IndexOf('\n')) < 0) { while (i < 0) { FillBucket(); i = bucket.IndexOf('\n'); } }
buf = bucket.Substring(0, i); bucket = bucket.Substring(i + 1);
return buf; }
/**//// <summary> /// 返回服务器端返回信息 /// </summary> private void ReadResponse() { string buf; messages = "";
while (true) { buf = GetLineFromBucket();
if (Regex.Match(buf, "^[0-9]+ ").Success) { responseStr = buf; response = int.Parse(buf.Substring(0, 3)); break; } else messages += Regex.Replace(buf, "^[0-9]+-", "") + "\n"; } }
/**//// <summary> /// 打开数据套接字 /// </summary> private void OpenDataSocket() { if (passive_mode) { string[] pasv; string server; int port;
Connect(); SendCommand("PASV"); ReadResponse(); if (response != 227) Fail();
try { int i1, i2;
i1 = responseStr.IndexOf('(') + 1; i2 = responseStr.IndexOf(')') - i1; pasv = responseStr.Substring(i1, i2).Split(','); } catch (Exception) { Disconnect(); errormessage += "Malformed PASV response: " + responseStr; return ; }
if (pasv.Length < 6) { Disconnect(); errormessage += "Malformed PASV response: " + responseStr; return ; }
server = String.Format("{0}.{1}.{2}.{3}", pasv[0], pasv[1], pasv[2], pasv[3]); port = (int.Parse(pasv[4]) << 8) + int.Parse(pasv[5]);
try { CloseDataSocket();
data_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
#if NET1 data_ipEndPoint = new IPEndPoint(Dns.GetHostByName(server).AddressList[0], port); #else data_ipEndPoint = new IPEndPoint(System.Net.Dns.GetHostEntry(server).AddressList[0], port); #endif
data_sock.Connect(data_ipEndPoint);
} catch (Exception ex) { errormessage += "Failed to connect for data transfer: " + ex.Message; return ; } } else { Connect();
try { CloseDataSocket();
listening_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 对于端口,则发送IP地址.下面则提取相应信息 string sLocAddr = main_sock.LocalEndPoint.ToString(); int ix = sLocAddr.IndexOf(':'); if (ix < 0) { errormessage += "Failed to parse the local address: " + sLocAddr; return; } string sIPAddr = sLocAddr.Substring(0, ix); // 系统自动绑定一个端口号(设置 port = 0) System.Net.IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(sIPAddr), 0);
listening_sock.Bind(localEP); sLocAddr = listening_sock.LocalEndPoint.ToString(); ix = sLocAddr.IndexOf(':'); if (ix < 0) { errormessage += "Failed to parse the local address: " + sLocAddr;
} int nPort = int.Parse(sLocAddr.Substring(ix + 1));
// 开始侦听链接请求 listening_sock.Listen(1); string sPortCmd = string.Format("PORT {0},{1},{2}", sIPAddr.Replace('.', ','), nPort / 256, nPort % 256); SendCommand(sPortCmd); ReadResponse(); if (response != 200) Fail(); } catch (Exception ex) { errormessage += "Failed to connect for data transfer: " + ex.Message; return; } } }
private void ConnectDataSocket() { if (data_sock != null) // 已链接 return;
try { data_sock = listening_sock.Accept(); // Accept is blocking listening_sock.Close(); listening_sock = null;
if (data_sock == null) { throw new Exception("Winsock error: " + Convert.ToString(System.Runtime.InteropServices.Marshal.GetLastWin32Error())); } } catch (Exception ex) { errormessage += "Failed to connect for data transfer: " + ex.Message; } }
private void CloseDataSocket() { if (data_sock != null) { if (data_sock.Connected) { data_sock.Close(); } data_sock = null; }
data_ipEndPoint = null; }
/**//// <summary> /// 关闭所有链接 /// </summary> public void Disconnect() { CloseDataSocket();
if (main_sock != null) { if (main_sock.Connected) { SendCommand("QUIT"); main_sock.Close(); } main_sock = null; }
if (file != null) file.Close();
main_ipEndPoint = null; file = null; }
/**//// <summary> /// 链接到FTP服务器 /// </summary> /// <param name="server">要链接的IP地址或主机名</param> /// <param name="port">端口号</param> /// <param name="user">登陆帐号</param> /// <param name="pass">登陆口令</param> public void Connect(string server, int port, string user, string pass) { this.server = server; this.user = user; this.pass = pass; this.port = port;
Connect(); }
/**//// <summary> /// 链接到FTP服务器 /// </summary> /// <param name="server">要链接的IP地址或主机名</param> /// <param name="user">登陆帐号</param> /// <param name="pass">登陆口令</param> public void Connect(string server, string user, string pass) { this.server = server; this.user = user; this.pass = pass;
Connect(); }
/**//// <summary> /// 链接到FTP服务器 /// </summary> public bool Connect() { if (server == null) { errormessage += "No server has been set.\r\n"; } if (user == null) { errormessage += "No server has been set.\r\n"; }
if (main_sock != null) if (main_sock.Connected) return true;
try { main_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); #if NET1 main_ipEndPoint = new IPEndPoint(Dns.GetHostByName(server).AddressList[0], port); #else main_ipEndPoint = new IPEndPoint(System.Net.Dns.GetHostEntry(server).AddressList[0], port); #endif
main_sock.Connect(main_ipEndPoint); } catch (Exception ex) { errormessage += ex.Message; return false; }
ReadResponse(); if (response != 220) Fail();
SendCommand("USER " + user); ReadResponse();
switch (response) { case 331: if (pass == null) { Disconnect(); errormessage += "No password has been set."; return false; } SendCommand("PASS " + pass); ReadResponse(); if (response != 230) { Fail(); return false; } break; case 230: break; }
return true; }
/**//// <summary> /// 获取FTP当前(工作)目录下的文件列表 /// </summary> /// <returns>返回文件列表数组</returns> public ArrayList List() { Byte[] bytes = new Byte[512]; string file_list = ""; long bytesgot = 0; int msecs_passed = 0; ArrayList list = new ArrayList();
Connect(); OpenDataSocket(); SendCommand("LIST"); ReadResponse();
switch (response) { case 125: case 150: break; default: CloseDataSocket(); throw new Exception(responseStr); } ConnectDataSocket();
while (data_sock.Available < 1) { System.Threading.Thread.Sleep(50); msecs_passed += 50;
if (msecs_passed > (timeout / 10)) { break; } }
while (data_sock.Available > 0) { bytesgot = data_sock.Receive(bytes, bytes.Length, 0); file_list += Encoding.ASCII.GetString(bytes, 0, (int)bytesgot); System.Threading.Thread.Sleep(50); }
CloseDataSocket();
ReadResponse(); if (response != 226) throw new Exception(responseStr);
foreach (string f in file_list.Split('\n')) { if (f.Length > 0 && !Regex.Match(f, "^total").Success) list.Add(f.Substring(0, f.Length - 1)); }
return list; }
/**//// <summary> /// 获取到文件名列表 /// </summary> /// <returns>返回文件名列表</returns> public ArrayList ListFiles() { ArrayList list = new ArrayList();
foreach (string f in List()) { if ((f.Length > 0)) { if ((f[0] != 'd') && (f.ToUpper().IndexOf("<DIR>") < 0)) list.Add(f); } }
return list; }
/**//// <summary> /// 获取路径列表 /// </summary> /// <returns>返回路径列表</returns> public ArrayList ListDirectories() { ArrayList list = new ArrayList();
foreach (string f in List()) { if (f.Length > 0) { if ((f[0] == 'd') || (f.ToUpper().IndexOf("<DIR>") >= 0)) list.Add(f); } }
return list; }
/**//// <summary> /// 获取原始数据信息. /// </summary> /// <param name="fileName">远程文件名</param> /// <returns>返回原始数据信息.</returns> public string GetFileDateRaw(string fileName) { Connect();
SendCommand("MDTM " + fileName); ReadResponse(); if (response != 213) { errormessage += responseStr; return ""; }
return (this.responseStr.Substring(4)); }
/**//// <summary> /// 得到文件日期. /// </summary> /// <param name="fileName">远程文件名</param> /// <returns>返回远程文件日期</returns> public DateTime GetFileDate(string fileName) { return ConvertFTPDateToDateTime(GetFileDateRaw(fileName)); }
private DateTime ConvertFTPDateToDateTime(string input) { if (input.Length < 14) throw new ArgumentException("Input Value for ConvertFTPDateToDateTime method was too short.");
//YYYYMMDDhhmmss": int year = Convert.ToInt16(input.Substring(0, 4)); int month = Convert.ToInt16(input.Substring(4, 2)); int day = Convert.ToInt16(input.Substring(6, 2)); int hour = Convert.ToInt16(input.Substring(8, 2)); int min = Convert.ToInt16(input.Substring(10, 2)); int sec = Convert.ToInt16(input.Substring(12, 2));
return new DateTime(year, month, day, hour, min, sec); }
/**//// <summary> /// 获取FTP上的当前(工作)路径 /// </summary> /// <returns>返回FTP上的当前(工作)路径</returns> public string GetWorkingDirectory() { //PWD - 显示工作路径 Connect(); SendCommand("PWD"); ReadResponse();
if (response != 257) { errormessage += responseStr; }
string pwd; try { pwd = responseStr.Substring(responseStr.IndexOf("\"", 0) + 1);//5); pwd = pwd.Substring(0, pwd.LastIndexOf("\"")); pwd = pwd.Replace("\"\"", "\""); // 替换带引号的路径信息符号 } catch (Exception ex) { errormessage += ex.Message; return null; }
return pwd; }
/**//// <summary> /// 跳转服务器上的当前(工作)路径 /// </summary> /// <param name="path">要跳转的路径</param> public bool ChangeDir(string path) { Connect(); SendCommand("CWD " + path); ReadResponse(); if (response != 250) { errormessage += responseStr; return false; } return true; }
/**//// <summary> /// 创建指定的目录 /// </summary> /// <param name="dir">要创建的目录</param> public void MakeDir(string dir) { Connect(); SendCommand("MKD " + dir); ReadResponse();
switch (response) { case 257: case 250: break; default: { errormessage += responseStr; break; } } }
/**//// <summary> /// 移除FTP上的指定目录 /// </summary> /// <param name="dir">要移除的目录</param> public void RemoveDir(string dir) { Connect(); SendCommand("RMD " + dir); ReadResponse(); if (response != 250) { errormessage += responseStr; return; ; } }
/**//// <summary> /// 移除FTP上的指定文件 /// </summary> /// <param name="filename">要移除的文件名称</param> public void RemoveFile(string filename) { Connect(); SendCommand("DELE " + filename); ReadResponse(); if (response != 250) { errormessage += responseStr; } }
/**//// <summary> /// 重命名FTP上的文件 /// </summary> /// <param name="oldfilename">原文件名</param> /// <param name="newfilename">新文件名</param> public void RenameFile(string oldfilename, string newfilename) { Connect(); SendCommand("RNFR " + oldfilename); ReadResponse(); if (response != 350) { errormessage += responseStr; } else { SendCommand("RNTO " + newfilename); ReadResponse(); if (response != 250) { errormessage += responseStr; } } }
/**//// <summary> /// 获得指定文件的大小(如果FTP支持) /// </summary> /// <param name="filename">指定的文件</param> /// <returns>返回指定文件的大小</returns> public long GetFileSize(string filename) { Connect(); SendCommand("SIZE " + filename); ReadResponse(); if (response != 213) { errormessage += responseStr; }
return Int64.Parse(responseStr.Substring(4)); }
/**//// <summary> /// 上传指定的文件 /// </summary> /// <param name="filename">要上传的文件</param> public bool OpenUpload(string filename) { return OpenUpload(filename, filename, false); }
/**//// <summary> /// 上传指定的文件 /// </summary> /// <param name="filename">本地文件名</param> /// <param name="remotefilename">远程要覆盖的文件名</param> public bool OpenUpload(string filename, string remotefilename) { return OpenUpload(filename, remotefilename, false); }
/**//// <summary> /// 上传指定的文件 /// </summary> /// <param name="filename">本地文件名</param> /// <param name="resume">如果存在,则尝试恢复</param> public bool OpenUpload(string filename, bool resume) { return OpenUpload(filename, filename, resume); }
/**//// <summary> /// 上传指定的文件 /// </summary> /// <param name="filename">本地文件名</param> /// <param name="remote_filename">远程要覆盖的文件名</param> /// <param name="resume">如果存在,则尝试恢复</param> public bool OpenUpload(string filename, string remote_filename, bool resume) { Connect(); SetBinaryMode(true); OpenDataSocket();
bytes_total = 0;
try { file = new FileStream(filename, FileMode.Open); } catch (Exception ex) { file = null; errormessage += ex.Message; return false; }
file_size = file.Length;
if (resume) { long size = GetFileSize(remote_filename); SendCommand("REST " + size); ReadResponse(); if (response == 350) file.Seek(size, SeekOrigin.Begin); }
SendCommand("STOR " + remote_filename); ReadResponse();
switch (response) { case 125: case 150: break; default: file.Close(); file = null; errormessage += responseStr; return false; } ConnectDataSocket();
return true; }
/**//// <summary> /// 下载指定文件 /// </summary> /// <param name="filename">远程文件名称</param> public void OpenDownload(string filename) { OpenDownload(filename, filename, false); }
/**//// <summary> /// 下载并恢复指定文件 /// </summary> /// <param name="filename">远程文件名称</param> /// <param name="resume">如文件存在,则尝试恢复</param> public void OpenDownload(string filename, bool resume) { OpenDownload(filename, filename, resume); }
/**//// <summary> /// 下载指定文件 /// </summary> /// <param name="filename">远程文件名称</param> /// <param name="localfilename">本地文件名</param> public void OpenDownload(string remote_filename, string localfilename) { OpenDownload(remote_filename, localfilename, false); }
/**//// <summary> /// 打开并下载文件 /// </summary> /// <param name="remote_filename">远程文件名称</param> /// <param name="local_filename">本地文件名</param> /// <param name="resume">如果文件存在则恢复</param> public void OpenDownload(string remote_filename, string local_filename, bool resume) { Connect(); SetBinaryMode(true);
bytes_total = 0;
try { file_size = GetFileSize(remote_filename); } catch { file_size = 0; }
if (resume && File.Exists(local_filename)) { try { file = new FileStream(local_filename, FileMode.Open); } catch (Exception ex) { file = null; throw new Exception(ex.Message); }
SendCommand("REST " + file.Length); ReadResponse(); if (response != 350) throw new Exception(responseStr); file.Seek(file.Length, SeekOrigin.Begin); bytes_total = file.Length; } else { try { file = new FileStream(local_filename, FileMode.Create); } catch (Exception ex) { file = null; throw new Exception(ex.Message); } }
OpenDataSocket(); SendCommand("RETR " + remote_filename); ReadResponse();
switch (response) { case 125: case 150: break; default: file.Close(); file = null; errormessage += responseStr; return; } ConnectDataSocket();
return; }
/**//// <summary> /// 上传文件(循环调用直到上传完毕) /// </summary> /// <returns>发送的字节数</returns> public long DoUpload() { Byte[] bytes = new Byte[512]; long bytes_got;
try { bytes_got = file.Read(bytes, 0, bytes.Length); bytes_total += bytes_got; data_sock.Send(bytes, (int)bytes_got, 0);
if (bytes_got <= 0) { //上传完毕或有错误发生 file.Close(); file = null;
CloseDataSocket(); ReadResponse(); switch (response) { case 226: case 250: break; default: //当上传中断时 { errormessage += responseStr; return -1; } }
SetBinaryMode(false); } } catch (Exception ex) { file.Close(); file = null; CloseDataSocket(); ReadResponse(); SetBinaryMode(false); //throw ex; //当上传中断时 errormessage += ex.Message; return -1; }
return bytes_got; }
/**//// <summary> /// 下载文件(循环调用直到下载完毕) /// </summary> /// <returns>接收到的字节点</returns> public long DoDownload() { Byte[] bytes = new Byte[512]; long bytes_got;
try { bytes_got = data_sock.Receive(bytes, bytes.Length, 0);
if (bytes_got <= 0) { //下载完毕或有错误发生 CloseDataSocket(); file.Close(); file = null;
ReadResponse(); switch (response) { case 226: case 250: break; default: { errormessage += responseStr; return -1; } }
SetBinaryMode(false);
return bytes_got; }
file.Write(bytes, 0, (int)bytes_got); bytes_total += bytes_got; } catch (Exception ex) { CloseDataSocket(); file.Close(); file = null; ReadResponse(); SetBinaryMode(false); //throw ex; //当下载中断时 errormessage += ex.Message; return -1; }
return bytes_got; }
#endregion }
有了核心代码,下面就是相关的FTP信息(如服务器站点,端口号,密码等)是如何在我们产品代 码中进行设置并保存呢? 请用Reflectort工具反射文件:discuz.config.dll,其配置类如下(其也采用序列化方式进行保 存): FTPConfigInfo [FTP配置信息类] FTPConfigInfoCollection [FTP配置信息类集合] 其中的FTPConfigInfo类的代码如下(我已将注释补充):  Code
/// <summary> /// FTP配置信息类 /// </summary> [Serializable] public class FTPConfigInfo : IConfigInfo {
#region FTP私有字段
private string m_name; //名称,如forumattach:论坛附件,spaceattach:空间附件,album:相册等
private string m_serveraddress; //服务器地址
private int m_serverport = 25; //服务器端口号
private string m_username; //登陆帐号
private string m_password; //登陆密码
private int m_mode = 1; //链接模式 1:被动 2:主动
private int m_allowupload; //允许FTP上传附件 0:不允许 1:允许
private string m_uploadpath; //上传路径
private int m_timeout; //无响应时间(FTP在指定时间内无响应),单位:秒
private string m_remoteurl; //远程访问 URL
private int m_reservelocalattach = 0; //是否保留本地附件. 0:不保留 1:保留
#endregion
public FTPConfigInfo() { }
#region 属性
/// <summary> /// 名称 /// </summary> public string Name { get { return m_name; } set { m_name = value; } }
/// <summary> /// FTP服务器名称 /// </summary> public string Serveraddress { get { return m_serveraddress; } set { m_serveraddress = value; } }
/// <summary> /// FTP端口号 /// </summary> public int Serverport { get { return m_serverport; } set { m_serverport = value; } }
/// <summary> /// 登陆帐号 /// </summary> public string Username { get { return m_username; } set { m_username = value; } }
/// <summary> /// 登陆密码 /// </summary> public string Password { get { return m_password; } set { m_password = value; } }
/// <summary> /// 链接模式 1:被动 2:主动 /// </summary> public int Mode { get { return m_mode <= 0 ? 1 : m_mode; } set { m_mode = value <= 0 ? 1 : m_mode; } }
/// <summary> /// 允许FTP上传附件 /// </summary> public int Allowupload { get { return m_allowupload; } set { m_allowupload = value; } }
/// <summary> /// 上传路径 /// </summary> public string Uploadpath { get { return m_uploadpath; } set { m_uploadpath = value; } }
/// <summary> /// 无响应时间(FTP在指定时间内无响应),单位:秒 /// </summary> public int Timeout { get { return m_timeout <= 0 ? 10 : m_timeout; } set { m_timeout = value <= 0 ? 10 : m_timeout; } }
/// <summary> /// 远程访问 URL /// </summary> public string Remoteurl { get { return m_remoteurl; } set { m_remoteurl = value; } }
/// <summary> /// 保留本地附件:0为不保留 1为保留 /// </summary> public int Reservelocalattach { get { return m_reservelocalattach; } set { m_reservelocalattach = value; } } #endregion
}
而上面的FTPConfigInfoCollection类是一个可序列化的集合类(为FTPConfigInfo类实例集合)。 这样做是因为如果当论坛,空间,相册等功能需要远程附件支持时,都需要各自的配置信息,而通过序列 化FTPConfigInfoCollection 便可获取或保存各个功能的相应配置信息(也便于日后扩展),其生成的序 列化信息格式如下(相应的节点信息对应上面的FTPConfigInfo类的属性字段):  Code
<?xml version="1.0"?>
<ArrayOfFTPConfigInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FTPConfigInfo> <Name>ForumAttach</Name> <Serveraddress>ftpserver</Serveraddress> <Serverport>21</Serverport> <Username>username</Username> <Password>password</Password> <Mode>1</Mode> <Allowupload>0</Allowupload> <Uploadpath>test</Uploadpath> <Timeout>10</Timeout> <Remoteurl>http://localhost/forumattach</Remoteurl> <Reservelocalattach>0</Reservelocalattach> </FTPConfigInfo> <FTPConfigInfo> <Name>SpaceAttach</Name> <Serveraddress>ftpserver</Serveraddress> <Serverport>21</Serverport> <Username>username</Username> <Password>password</Password> <Mode>1</Mode> <Allowupload>0</Allowupload> <Uploadpath>test</Uploadpath> <Timeout>10</Timeout> <Remoteurl>http://localhost/spaceattach</Remoteurl> <Reservelocalattach>0</Reservelocalattach> </FTPConfigInfo> <FTPConfigInfo> <Name>AlbumAttach</Name> <Serveraddress>ftpserver</Serveraddress> <Serverport>21</Serverport> <Username>username</Username> <Password>password</Password> <Mode>1</Mode> <Allowupload>0</Allowupload> <Uploadpath>test</Uploadpath> <Timeout>10</Timeout> <Remoteurl>http://localhost/albumattach</Remoteurl> <Reservelocalattach>0</Reservelocalattach> </FTPConfigInfo>
</ArrayOfFTPConfigInfo>
而该序列化配置文件位于discuz.web项目的config\ftp.config下。 当然上面所说的只是相应的配置类,而为了便于前台开发相应功能,我对上面所说的FTP和配置类又进行了一次 类封装,并将类命名为FTPs,放在了discuz.forum下,大家可以用Reflector获得其代码,我在这里将注释补充如下:  Code
/// <summary> /// FTP操作类 /// </summary> public class FTPs { #region 声明上传信息(静态)对象
private static FTPConfigInfo m_forumattach;
private static FTPConfigInfo m_spaceattach;
private static FTPConfigInfo m_albumattach;
private static FTPConfigInfo m_mallattach;
#endregion
private static string m_configfilepath = Utils.GetMapPath(BaseConfigs.GetForumPath + "config/ftp.config"); /// <summary> /// 程序刚加载时ftp.config文件修改时间 /// </summary> private static DateTime m_fileoldchange;
/// <summary> /// 最近ftp.config文件修改时间 /// </summary> private static DateTime m_filenewchange;
private static object lockhelper = new object();
/// <summary> /// FTP信息枚举类型 /// </summary> public enum FTPUploadEnum { ForumAttach = 1, //论坛附件 SpaceAttach = 2, //空间附件 AlbumAttach = 3, //相册附件 MallAttach = 4 //商场附件 }
/// <summary> /// 静态构造函数(用于初始化对象和变量) /// </summary> static FTPs() { SetFtpConfigInfo(); m_fileoldchange = System.IO.File.GetLastWriteTime(m_configfilepath); }
/// <summary> /// FTP配置文件监视方法 /// </summary> private static void FtpFileMonitor() { //获取文件最近修改时间 m_filenewchange = System.IO.File.GetLastWriteTime(m_configfilepath); //当ftp.config修改时间发生变化时 if (m_fileoldchange != m_filenewchange) { lock (lockhelper) { if (m_fileoldchange != m_filenewchange) { //当文件发生修改(时间变化)则重新设置相关FTP信息对象 SetFtpConfigInfo(); m_fileoldchange = m_filenewchange; } } } }
/// <summary> /// 设置FTP对象信息 /// </summary> private static void SetFtpConfigInfo() {
FTPConfigInfoCollection ftpconfiginfocollection =
(FTPConfigInfoCollection)SerializationHelper.Load(typeof(FTPConfigInfoCollection), m_configfilepath);
FTPConfigInfoCollection.FTPConfigInfoCollectionEnumerator fcice = ftpconfiginfocollection.GetEnumerator();
//遍历集合并设置相应的FTP信息(静态)对象 while (fcice.MoveNext()) { if (fcice.Current.Name == "ForumAttach") { m_forumattach = fcice.Current; continue; }
if (fcice.Current.Name == "SpaceAttach") { m_spaceattach = fcice.Current; continue; }
if (fcice.Current.Name == "AlbumAttach") { m_albumattach = fcice.Current; continue; }
if (fcice.Current.Name == "MallAttach") { m_mallattach = fcice.Current; continue; } } }
/// <summary> /// 论坛附件FTP信息 /// </summary> public static FTPConfigInfo GetForumAttachInfo { get { FtpFileMonitor(); return m_forumattach; } }
/// <summary> /// 空间附件FTP信息 /// </summary> public static FTPConfigInfo GetSpaceAttachInfo { get { FtpFileMonitor(); return m_spaceattach; } }
/// <summary> /// 相册附件FTP信息 /// </summary> public static FTPConfigInfo GetAlbumAttachInfo { get { FtpFileMonitor(); return m_albumattach; } }
/// <summary> /// 相册附件FTP信息 /// </summary> public static FTPConfigInfo GetMallAttachInfo { get { FtpFileMonitor(); return m_mallattach; } }
#region 异步FTP上传文件
private delegate bool delegateUpLoadFile(string path, string file, FTPUploadEnum ftpuploadname);
//异步FTP上传文件代理 private delegateUpLoadFile upload_aysncallback;
public void AsyncUpLoadFile(string path, string file, FTPUploadEnum ftpuploadname) { upload_aysncallback = new delegateUpLoadFile(UpLoadFile); upload_aysncallback.BeginInvoke(path, file, ftpuploadname, null, null); }
#endregion
/// <summary> /// 普通FTP上传文件 /// </summary> /// <param name="file">要FTP上传的文件</param> /// <returns>上传是否成功</returns> public bool UpLoadFile(string path, string file, FTPUploadEnum ftpuploadname) { FTP ftpupload = new FTP(); //转换路径分割符为"/" path = path.Replace("""", "/"); path = path.StartsWith("/") ? path : "/" +path ;
//删除file参数文件 bool delfile = true; //根据上传名称确定上传的FTP服务器 switch (ftpuploadname) { //论坛附件 case FTPUploadEnum.ForumAttach: {
ftpupload = new FTP(m_forumattach.Serveraddress, m_forumattach.Serverport,
m_forumattach.Username, m_forumattach.Password, m_forumattach.Timeout);
path = m_forumattach.Uploadpath + path; delfile = (m_forumattach.Reservelocalattach == 1) ? false : true; break; }
//空间附件 case FTPUploadEnum.SpaceAttach: {
ftpupload = new FTP(m_spaceattach.Serveraddress, m_spaceattach.Serverport,
m_spaceattach.Username, m_spaceattach.Password, m_spaceattach.Timeout);
path = m_spaceattach.Uploadpath + path; delfile = (m_spaceattach.Reservelocalattach == 1) ? false : true; break; }
//相册附件 case FTPUploadEnum.AlbumAttach: {
ftpupload = new FTP(m_albumattach.Serveraddress, m_albumattach.Serverport,
m_albumattach.Username, m_albumattach.Password, m_albumattach.Timeout);
path = m_albumattach.Uploadpath + path; delfile = (m_albumattach.Reservelocalattach == 1) ? false : true; break; } //商城附件 case FTPUploadEnum.MallAttach: {
ftpupload = new FTP(m_mallattach.Serveraddress, m_mallattach.Serverport,
m_mallattach.Username, m_mallattach.Password, m_mallattach.Timeout);
path = m_mallattach.Uploadpath + path; delfile = (m_mallattach.Reservelocalattach == 1) ? false : true; break; } }
//切换到指定路径下,如果目录不存在,将创建 if (!ftpupload.ChangeDir(path)) { //ftpupload.MakeDir(path); foreach (string pathstr in path.Split('/')) { if (pathstr.Trim() != "") { ftpupload.MakeDir(pathstr); ftpupload.ChangeDir(pathstr); } } } ftpupload.Connect();
if (!ftpupload.IsConnected) { return false; }
|