wocaosinima 发表于 2015-5-30 07:27:30

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
/**////
    /// FTP类
    ///
    public class FTP
    {
      变量声明#region 变量声明

      /**////
      /// 服务器连接地址
      ///
      public string server;

      /**////
      /// 登陆帐号
      ///
      public string user;

      /**////
      /// 登陆口令
      ///
      public string pass;

      /**////
      /// 端口号
      ///
      public int port;

      /**////
      /// 无响应时间(FTP在指定时间内无响应)
      ///
      public int timeout;

      /**////
      /// 服务器错误状态信息
      ///
      public string errormessage;

   
      /**////
      /// 服务器状态返回信息
      ///
      private string messages;

      /**////
      /// 服务器的响应信息
      ///
      private string responseStr;

      /**////
      /// 链接模式(主动或被动,默认为被动)
      ///
      private bool passive_mode;      

      /**////
      /// 上传或下载信息字节数
      ///
      private long bytes_total;

      /**////
      /// 上传或下载的文件大小
      ///
      private long file_size;

      /**////
      /// 主套接字
      ///
      private Socket main_sock;

      /**////
      /// 要链接的网络地址终结点
      ///
      private IPEndPoint main_ipEndPoint;

      /**////
      /// 侦听套接字
      ///
      private Socket listening_sock;

      /**////
      /// 数据套接字
      ///
      private Socket data_sock;

      /**////
      /// 要链接的网络数据地址终结点
      ///
      private IPEndPoint data_ipEndPoint;

      /**////
      /// 用于上传或下载的文件流对象
      ///
      private FileStream file;

      /**////
      /// 与FTP服务器交互的状态值
      ///
      private int response;

      /**////
      /// 读取并保存当前命令执行后从FTP服务器端返回的数据信息
      ///
      private string bucket;

      #endregion

      构造函数#region 构造函数

      /**////
      /// 构造函数
      ///
      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 = "";
      }

      /**////
      /// 构造函数
      ///
      /// 服务器IP或名称
      /// 登陆帐号
      /// 登陆口令
      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 = "";
      }
      
      /**////
      /// 构造函数
      ///
      /// 服务器IP或名称
      /// 端口号
      /// 登陆帐号
      /// 登陆口令
      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 = "";
      }


      /**////
      /// 构造函数
      ///
      /// 服务器IP或名称
      /// 端口号
      /// 登陆帐号
      /// 登陆口令
      /// 链接方式
      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 = modetimeout)
                {
                  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;
      }


      /**////
      /// 返回服务器端返回信息
      ///
      private void ReadResponse()
      {
            string buf;
            messages = "";

            while (true)
            {
                buf = GetLineFromBucket();

                if (Regex.Match(buf, "^+ ").Success)
                {
                  responseStr = buf;
                  response = int.Parse(buf.Substring(0, 3));
                  break;
                }
                else
                  messages += Regex.Replace(buf, "^+-", "") + "\n";
            }
      }


      /**////
      /// 打开数据套接字
      ///
      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, pasv, pasv, pasv);
                port = (int.Parse(pasv)(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;
      }

      /**////
      /// 获取到文件名列表
      ///
      /// 返回文件名列表
      public ArrayList ListFiles()
      {
            ArrayList list = new ArrayList();

            foreach (string f in List())
            {
                if ((f.Length > 0))
                {
                  if ((f != 'd') && (f.ToUpper().IndexOf("") < 0))
                        list.Add(f);
                }
            }

            return list;
      }

      /**////
      /// 获取路径列表
      ///
      /// 返回路径列表
      public ArrayList ListDirectories()
      {
            ArrayList list = new ArrayList();

            foreach (string f in List())
            {
                if (f.Length > 0)
                {
                  if ((f == 'd') || (f.ToUpper().IndexOf("") >= 0))
                        list.Add(f);
                }
            }

            return list;
      }

      /**////
      /// 获取原始数据信息.
      ///
      /// 远程文件名
      /// 返回原始数据信息.
      public string GetFileDateRaw(string fileName)
      {
            Connect();

            SendCommand("MDTM " + fileName);
            ReadResponse();
            if (response != 213)
            {
                errormessage += responseStr;
                return "";
            }

            return (this.responseStr.Substring(4));
      }

      /**////
      /// 得到文件日期.
      ///
      /// 远程文件名
      /// 返回远程文件日期
      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);
      }

      /**////
      /// 获取FTP上的当前(工作)路径
      ///
      /// 返回FTP上的当前(工作)路径
      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;
      }


      /**////
      /// 跳转服务器上的当前(工作)路径
      ///
      /// 要跳转的路径
      public bool ChangeDir(string path)
      {
            Connect();
            SendCommand("CWD " + path);
            ReadResponse();
            if (response != 250)
            {
                errormessage += responseStr;
                return false;
            }
            return true;
      }

      /**////
      /// 创建指定的目录
      ///
      /// 要创建的目录
      public void MakeDir(string dir)
      {
            Connect();
            SendCommand("MKD " + dir);
            ReadResponse();

            switch (response)
            {
                case 257:
                case 250:
                  break;
                default:
                  {
                        errormessage += responseStr;
                        break;
                  }
            }
      }

      /**////
      /// 移除FTP上的指定目录
      ///
      /// 要移除的目录
      public void RemoveDir(string dir)
      {
            Connect();
            SendCommand("RMD " + dir);
            ReadResponse();
            if (response != 250)
            {
                errormessage += responseStr;
                return; ;
            }
      }

      /**////
      /// 移除FTP上的指定文件
      ///
      /// 要移除的文件名称
      public void RemoveFile(string filename)
      {
            Connect();
            SendCommand("DELE " + filename);
            ReadResponse();
            if (response != 250)
            {
                errormessage += responseStr;
            }
      }

      /**////
      /// 重命名FTP上的文件
      ///
      /// 原文件名
      /// 新文件名
      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;
                }
            }
      }

      /**////
      /// 获得指定文件的大小(如果FTP支持)
      ///
      /// 指定的文件
      /// 返回指定文件的大小
      public long GetFileSize(string filename)
      {
            Connect();
            SendCommand("SIZE " + filename);
            ReadResponse();
            if (response != 213)
            {
                errormessage += responseStr;
            }

            return Int64.Parse(responseStr.Substring(4));
      }

      /**////
      /// 上传指定的文件
      ///
      /// 要上传的文件
      public bool OpenUpload(string filename)
      {
            return OpenUpload(filename, filename, false);
      }

      /**////
      /// 上传指定的文件
      ///
      /// 本地文件名
      /// 远程要覆盖的文件名
      public bool OpenUpload(string filename, string remotefilename)
      {
            return OpenUpload(filename, remotefilename, false);
      }

      /**////
      /// 上传指定的文件
      ///
      /// 本地文件名
      /// 如果存在,则尝试恢复
      public bool OpenUpload(string filename, bool resume)
      {
            return OpenUpload(filename, filename, resume);
      }

      /**////
      /// 上传指定的文件
      ///
      /// 本地文件名
      /// 远程要覆盖的文件名
      /// 如果存在,则尝试恢复
      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;
      }

      /**////
      /// 下载指定文件
      ///
      /// 远程文件名称
      public void OpenDownload(string filename)
      {
            OpenDownload(filename, filename, false);
      }

      /**////
      /// 下载并恢复指定文件
      ///
      /// 远程文件名称
      /// 如文件存在,则尝试恢复
      public void OpenDownload(string filename, bool resume)
      {
            OpenDownload(filename, filename, resume);
      }

      /**////
      /// 下载指定文件
      ///
      /// 远程文件名称
      /// 本地文件名
      public void OpenDownload(string remote_filename, string localfilename)
      {
            OpenDownload(remote_filename, localfilename, false);
      }

      /**////
      /// 打开并下载文件
      ///
      /// 远程文件名称
      /// 本地文件名
      /// 如果文件存在则恢复
      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;
      }

      /**////
      /// 上传文件(循环调用直到上传完毕)
      ///
      /// 发送的字节数
      public long DoUpload()
      {
            Byte[] bytes = new Byte;
            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
页: [1]
查看完整版本: Discuz!NT中远程附件的功能实现[FTP协议]