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信息(如服务器站点,端口号,密码等)是如何在我们产品代

码中进行设置并保存呢?

     请用Reflectort工具反射文件:discuz.config.dll,其配置类如下(其也采用序列化方式进行保

存):


     FTPConfigInfo   [FTP配置信息类]
     FTPConfigInfoCollection [FTP配置信息类集合]

    其中的FTPConfigInfo类的代码如下(我已将注释补充):
   

///   <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类的属性字段):


<? 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获得其代码,我在这里将注释补充如下:



  ///   <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 ;
            }
            
int  perc  =   0 ;

            
// 绑定要上传的文件
             if  ( ! ftpupload.OpenUpload(file, System.IO.Path.GetFileName(file)))
            {
                ftpupload.Disconnect();
                
return   false ;
            }

            
// 开始进行上传
             while  (ftpupload.DoUpload()  >   0 )
            {
                perc 
=  ( int )(((ftpupload.BytesTotal)  *   100 /  ftpupload.FileSize);
            }

            ftpupload.Disconnect();

            
// (如存在)删除指定目录下的文件
             if  (delfile  &&  Utils.FileExists(file))
            {
                System.IO.File.Delete(file);
            }
          

            
if  (perc  >=   100 )
            {
                
return   true ;
            }
            
else
            {
                
return   false ;
            }
        }

        
///   <summary>
        
///  FTP连接测试
        
///   </summary>
        
///   <param name="Serveraddress"> FTP服务器地址 </param>
        
///   <param name="Serverport"> FTP端口 </param>
        
///   <param name="Username"> 用户名 </param>
        
///   <param name="Password"> 密码 </param>
        
///   <param name="Timeout"> 超时时间(秒) </param>
        
///   <param name="uploadpath"> 附件保存路径 </param>
        
///   <param name="message"> 返回信息 </param>
        
///   <returns> 是否可用 </returns>

        public bool TestConnect(string Serveraddress, int Serverport, string Username, string Password, 

int Timeout, string uploadpath, ref string message)

 

 

        {
            FTP ftpupload 
=   new  FTP(Serveraddress, Serverport, Username, Password, Timeout);
            
bool  isvalid  =  ftpupload.Connect();
            
if  ( ! isvalid)
            {
                message 
=  ftpupload.errormessage;
                
return  isvalid;
            }

            
// 切换到指定路径下,如果目录不存在,将创建
             if  ( ! ftpupload.ChangeDir(uploadpath))
            {
                ftpupload.MakeDir(uploadpath);
                
if  ( ! ftpupload.ChangeDir(uploadpath))
                {
                    message 
+=  ftpupload.errormessage;
                    isvalid 
=   false ;
                }
            }
            
            
return  isvalid;
        }

     }

     现在万事俱备,我们要将新增的的远程附件功能的调用代码放到原有的附件上传代码中了,这里以
" ForumUtils.cs "
文件(位于discuz.forum)为例, 通过Reflector, 查看该类下的SaveRequestFiles方法中的如下代码段(已添加注释):

public static AttachmentInfo[] SaveRequestFiles(int forumid, int MaxAllowFileCount, int MaxSizePerDay, 

int MaxFileSize, int TodayUploadedSize, string AllowFileType, int watermarkstatus, 

GeneralConfigInfo config, string filekey)

 

 

{    

     
// 当支持FTP上传附件时,使用FTP上传远程附件
                             if  (FTPs.GetForumAttachInfo.Allowupload  ==   1 )
                            {
                                FTPs ftps 
=   new  FTPs();

                                
// 当不保留本地附件模式时,在上传完成之后删除本地tempfilename文件
                                 if  (FTPs.GetForumAttachInfo.Reservelocalattach  ==   0 )
                                {

                                    ftps.UpLoadFile(newfilename.Substring(0, newfilename.LastIndexOf("""")), 

UploadDir + tempfilename, FTPs.FTPUploadEnum.ForumAttach);

 

 

                                }
                                
else
                                {

                                    ftps.UpLoadFile(newfilename.Substring(0, newfilename.LastIndexOf("""")), 

UploadDir + newfilename, FTPs.FTPUploadEnum.ForumAttach);

 

 

                                }
                            }

     
}

     当然这里只是列举了一处改动,其实还有几处不小的变化也是相似的代码实现,比如空间或相册中进行图片等
附件上传时。


     而这些功能的设置可以到"后台管理"中找到,如下图所示:

 

 




     好了,今天的内容就先到这里了,感兴趣的朋友可以在回复中或用EMAIL的方法与我联系。

     老外的FTP类源码下载链接,请点击这里:)


     作者:代震军 (daizhj)      

     博客:http://daizhj.cnblogs.com 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值