[转]FCKEditorSimpleNet 2.65 (FCKEditor源代码分析及.Net简化版本)

本文基于.Net的应用针对FCKEditor的源代码进行分析,并对其部分功能进行了简化和重构,特别是上传功能,旨在提供更简洁、安全且易于使用的解决方案。包括插入/编辑超链接、FLASH、表格和图片等功能的优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

FCKEditor 是一款开源的非常优秀的WEB在线编辑器,它的JS类库几乎匹敌于当前流行的JQuery,目前最新版本为2.65,官方网站:http://www.fckeditor.net/ 。本文基于.Net的应用针对FCKEditor的源代码进行分析,同时改造了部分过于复杂的功能,将其简单化。供大家学习讨论之用,个人能力有限,不足之处还请指正。

DEMO环境:.Net Framework 3.5,Visual Studio 2008,Client:FCKEditor 2.6.5,Server:FCKEditor.Net 2.6.3

 

插入/编辑超链接

原始: 

o_fck1.gif

 简化:

o_fck2.gif 

如上图所示,对“插入/编辑超链接”功能进行了彻底简化,不可否认源代码中考虑了超链接的几乎所有应用情景,非常之完善和全面,甚至提供了服务器目录的浏览和文件上传功能,但这也恰恰暴露了更多的安全隐患,参考了各大网站的应用,简化为仅显示文本和链接地址。

这部分功能的源代码主要分布在两个文件中:

UI部分 /editor/dialog/fck_link.html

功能函数 /editor/dialog/fck_link/fck_link.js

因为原始版本包含了过于复杂的功能,所以在分析和修改js代码时也费了不少功夫,删除了多余的功能函数,支持了文本的显示,缩减大约90%的代码。具体细节可查看DEMO。

插入/编辑FLASH

原始: 

o_fck3.gif

简化:

o_fck4.gif 

如上图所示, 去除了服务器浏览和文件上传功能;去除了“高级”里的多余选项,将功能整合在一个标签下。

这部分功能源代码分布在三个文件中:

UI界面 /editor/dialog/fck_flash.html

功能函数 /editor/dialog/fck_flash/fck_flash.js

Flash预览 /editor/dialog/fck_flash/fck_flash_preview.html

插入/编辑表格

原始:

o_fck5.gif

简化:

o_fck6.gif

该部分功能主要包含在 /editor/dialog/fck_table.html 中,如上图,去除了“标题”、“摘要”、“标题单元格”三项,通过生成的HTML代码可以发现,FCKEditor是一款非常严谨的软件,它严格遵循了W3C。但大部开发人员都不熟悉的HTML标签对于用户来说使用就更较少了,固去除了这三项,原始与简化后控件生成的HTML差别如下:

原始:

ContractedBlock.gif ExpandedBlockStart.gif 代码
< table  border ="1"  cellspacing ="1"   summary ="summary"  cellpadding ="1"  width ="200"  align ="center"  height ="200" >
    
< caption > caption </ caption >
    
< thead >
        
< tr >
            
< th scope ="col" > &nbsp; </ th >
            
< th scope ="col" > &nbsp; </ th >
        
</ tr >
    
</ thead >
    
< tbody >
        
< tr >
            
< td > &nbsp; </ td >
            
< td > &nbsp; </ td >
        
</ tr >
    
</ tbody >
</ table >

简化:

ContractedBlock.gif ExpandedBlockStart.gif 代码
< table  border ="1"  cellspacing ="1"  cellpadding ="1"  width ="200"  align ="center"  height ="200" >
    
< tbody >
        
< tr >
            
< td > &nbsp; </ td >
            
< td > &nbsp; </ td >
        
</ tr >
        
< tr >
            
< td > &nbsp; </ td >
            
< td > &nbsp; </ td >
        
</ tr >
    
</ tbody >
</ table >

插入/编辑图片

原始:

o_fck_7.gif 

简化:

o_fck_8.gif 

一直对 FCKEditor的上传功能不满意,首先是安全问题,“服务器浏览”这项功能之前就曾暴露过严重安全漏洞,所以完全屏蔽了这项功能,彻底删除了 /editor/filemanager/browser 目录;

其次是上传代码的扩展受限,比如上传图片时生成缩略图,实现这样一个常见的附加功能在FCKEditor现有结构下困难重重;

再有配置复杂且冗余,比如我的项目中十个页面用到了FCKEditor,每处的上传处理逻辑均不相同,这种需求难道要放十个FCK客户端十个配置?

在此并非刻意将FCK的上传批的一无是处,恰恰相反,我倒觉得FCK的开发团队是绝对的完美主义者,如果仔细研究了源代码你就会毫不怀疑这一点。他们要把这个控件做的支持多语言,多功能,又要保持绝对一至性。过于追求完美必然就会带来其它的牺牲。

OK,我决定重写FCKEditor的图片上传代码框架,仅争对.Net,目标是让开发者最简单最便捷的处理用户的上传。

概述一下我的想法:删除FCKEditor 中一切上传相关的配置,而只需指定一个参数:“我的上传处理类”。FCK会将上传交由这个类来处理,在这个类里你可以随心所欲的添加任何代码,权限控制、与数据库交互、图片处理、存放位置......所有逻辑均在这个类里实现,然后告诉FCK这个类的位置即可。下面详细描述现实的过程:

Step1.让FCKEditor将上传交由“我的上传处理类”

首先为FCKEditor添加属性“ImageUploadAssembly” ,这个属性告诉FCK“我的上传处理类”的位置,格式为“程序集名称,类名称”,后面将会在FCK中扩展代码利用反射来创建该类的对象,并将上传交由这个对象处理。在服务端代码FCKEditor.cs中添加属性:

ContractedBlock.gif ExpandedBlockStart.gif C#代码
///   <summary>
        
///  用于指明当前控件上传图片处理的类
        
///  格式为“程序集名称,类名称”
        
///   </summary>
        [DefaultValue( "" )]
        
public   string  ImageUploadAssembly
        {
            
get
            {
                
object  o  =  ViewState[ " ImageUploadAssembly " ];
                
return  (o  ==   null   ?   ""  : ( string )o);
            }

            
set
            {
                ViewState[ " ImageUploadAssembly " =  value;                
            }
        }

接着在页面上添加FCKEditor控件时就能指定该属性了,如下代码所示:

ContractedBlock.gif ExpandedBlockStart.gif HTML代码
< div >
< FCKeditorV2:FCKeditor 
ID ="fck"  runat ="server"  
BasePath ="/fckeditorsimplenet265/"  
Height ="400px"  
ToolbarSet ="Default"   ImageUploadAssembly="FCKEditorSimpleNet.Web,FCKEditorSimpleNet.Web.MyImageUploadHandler.Default" >
</ FCKeditorV2:FCKeditor >
</ div >

然后是一个关键:如何将属性值传递到FCK中的Upload.cs中?花了不少时间对FCK源代码进行研究,整理出如下步骤:

1、首先将属性值以URL方式传递到 /editor/fckeditor.html中,如 /editor/fckeditor.html?ImageUploadAssembly=FCKEditorSimpleNet.Web,FCKEditorSimpleNet.Web.MyImageUploadHandler.Default ,这一步需要修改服务端Fckeditor.cs,如下:

ContractedBlock.gif ExpandedBlockStart.gif C#代码
protected   override   void  Render(HtmlTextWriter writer)
{
  
// ....部分代码略
   if  ( _IsCompatible )
  {
    
// ....部分代码略

    
// 将图片上传处理程序集,类名称作为参数传递到 Fckeditor.html
     if  ( this .ImageUploadAssembly.Length  >   0 )
      sLink  +=   " &amp;ImageUploadAssembly= "   +   this .ImageUploadAssembly;
  }
}

2、从 fckeditor.html 将参数传递到 /editor/fckdialog.html ,这个过程包含在 /editor/js/fckeditorcode_gecko.js 和 /editor/js/fckeditorcode_ie.js 中,这两个js是经过min处理的,分别对应了IE和非IE浏览器,两个文件基本一至,只是JS写法上稍有差别,修改如下:

O.src = FCKConfig.BasePath + ' fckdialog.html?ImageUploadAssembly= ' + (FCKURLParams[ ' ImageUploadAssembly ' ] || '' );

3、从 fckdialog.html 将参数传递到 /editor/dialog/fck_image.html

在fckdialog.html定义函数getUrlParameter,该函数供iframe子页面调用,以获取Url参数值:

ContractedBlock.gif ExpandedBlockStart.gif JS代码
// 获取fckdialog.html的参数< /span>
function getUrlParameter(pName){
    
var pVal = "";
    
var aParams = document.location.search.substr(1).split('&');
    
if(aParams.length > 0){
        
for(var i = 0; i < aParams.length; i++ ){
            
var aParam = aParams[i].split('=') ;
            
var sParamName  = decodeURIComponent( aParam[0] );
            
var sParamValue = decodeURIComponent( aParam[1] );
            
if(pName.toUpperCase() == sParamName.toUpperCase()){
                pVal = sParamValue;
            }            
        }
    }
    
return pVal;
}

4、从 fck_image.html 将参数传递到 Uploader.cs

fck_image.html 会将图片放在一个INPUT中,然后POST到服务端(ASPX),INPUT的名称为NewFile,服务端ASPX对应的Class就是Uploader.cs,现在要添加一个ImageUploadAssembly属性值一并提交给服务端,在此使用GET方式在URL后附加一个参数,修改 /editor/dialog/fck_image/fck_image.js 的 window.onload 函数,将参数值附加到 form.action,如下:

ContractedBlock.gif ExpandedBlockStart.gif JS代码
window.onload  =   function ()
{
    
// ....部分代码略
    GetE( ' frmUpload ' ).action  =  FCKConfig.BasePath  +   " filemanager/image/upload.aspx?ImageUploadAssembly= "   +  dialog.getUrlParameter( " ImageUploadAssembly " );
}

OK,至此在Uploader.cs里用Request.QueryString["ImageUploadAssembly"]就能获取到我们设置的属性值了。

接下来是根据该属性的值动态构造出对象,这里利用了.Net的反射,帖出Uploader.cs代码:

ContractedBlock.gif ExpandedBlockStart.gif 代码
using  System;
using  System.Reflection;
using  System.Web;

namespace  FredCK.FCKeditorV2.ImageUpload
{
    
///   <summary>
    
///  图片上传接口
    
///  接收GET参数 ImageUploadAssembly,根据参数值反射出自定义图片处理对象
    
///   </summary>
     public   class  Uploader : System.Web.UI.Page
    {
        
protected   override   void  OnLoad(EventArgs e)
        {
            
if  (Request.QueryString[ " ImageUploadAssembly " !=   null )
            {
                
string  ass  =  Request.QueryString[ " ImageUploadAssembly " ].Trim();
                
if  (ass  !=   string .Empty)
                {
                    
string [] assArray  =  ass.Split( ' , ' );

                    
if  (assArray.Length  ==   2 )
                    {
                        
if  (assArray[ 0 ].Trim()  !=   string .Empty  &&  assArray[ 1 ].Trim()  !=   string .Empty)
                        {
                            
// 获取上传文件
                            HttpPostedFile hpf  =  Request.Files[ " NewFile " ];
                            
if  (hpf  !=   null )
                            {
                                FredCK.FCKeditorV2.ImageUpload.Base customUploader  =   null ;
                                
bool  ok  =   true ;

                                
try
                                {
                                    customUploader  =  (FredCK.FCKeditorV2.ImageUpload.Base) this .createObject(assArray[ 0 ].Trim(), assArray[ 1 ].Trim());
                                    customUploader.PostFile  =  hpf;
                                }
                                
catch
                                {
                                    ok  =   false ;
                                    FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse( false "" " 自定义处理类不存在或设置的值不正确!< /span>");
                                }

                                
if (ok)
                                {
                                    
//调用自定义类中的Save方法< /span>
                                    customUploader.Save();                                    
                                }
                            }
                            
else
                            {
                                FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse(false"""未获取上传文件!");
                            }
                        }
                        
else
                        {
                            FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse(false"""参数有误!");
                        }
                    }
                    
else
                    {
                        FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse(false"""参数有误!");
                    }
                }
                
else
                {
                    FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse(false"""参数有误!");
                }
            }
            
else
            {
                FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse(false"""参数有误!");
            }
        }

        
/// <summary>
        
/// 反射创建对象
        
/// </summary>
        
/// <param name="assemblyName">程序集名称</param>
        
/// <param name="classFullName">完整类名称</param>
        
/// <returns></returns>
        private object createObject(string assemblyName, string classFullName)
        {
            
return Assembly.Load(assemblyName).CreateInstance(classFullName);
        }
    }
}

至此上传逻辑告一段落,因前面的描述和代码即有客户端的又有服务端的,并且只是核心代码片断,不知各位有没有看明白。总结一下这部分的逻辑:在客户端定义一个参数,将该参数传递到服务端,服务端根据参数值构造对象。如下图所示:

o_fck_uml_1.jpg 

Step2.自定义上传处理类的约束

很庆幸可以在.Net里用一个类来处理用户的上传,并且有反射这样的技术来动态构造对象。这段时间在写一些ANSI C,我想也只有在写C的时候才能越发体会C#、高级语言、OOP的好。所有代码都是对现实一件事情的抽象,用C#你的代码可以跟思维同步,而如果用C你就得考虑计算机是否明白和接受你的思维和代码了。

进入正题,分析一下自定义上传处理类的逻辑:首先得接收文件,.Net中是一个HttpPostedFile对象;然后对文件进行处理、保存等系列操作;最后将结果告诉FCKEditor,由它来控制UI及用户交互。把这些必需的逻辑封装成一个抽象父类,每个用户自定义的上传处理类必需从该类继承,并实现相应的抽象方法:

ContractedBlock.gif ExpandedBlockStart.gif 代码
using  System;
using  System.Web;

namespace  FredCK.FCKeditorV2.ImageUpload
{
    
///   <summary>
    
///  图片上传的抽象基类
    
///   </summary>
     public   abstract   class  Base : System.Web.UI.Page
    {
        
private  System.Web.HttpPostedFile mPostFile;

        
///   <summary>
        
///  上传文件
        
///   </summary>
         public  System.Web.HttpPostedFile PostFile {
            
get  {  return   this .mPostFile; }
            
set  {  this .mPostFile  =  value; }
        }

        
///   <summary>
        
///  文件上传结束回调客户端
        
///   </summary>
        
///   <param name="isSucceed"> 上传是否成功 </param>
        
///   <param name="fileUrl"> 上传完成后该文件的URL< /span></param>
        
/// <param name="customMsg">自定义消息</param>
        public static void SendFileUploadResponse(bool isSucceed, string fileUrl, string customMsg)
        {
            System.Web.HttpContext.Current.Response.Clear();
            System.Web.HttpContext.Current.Response.Write("<script type='text/javascript'>");
            System.Web.HttpContext.Current.Response.Write(@"(function(){var d=document.domain;while (true){try{var A=window.top.opener.document.domain;break;}catch(e) {};d=d.replace(/.*?(?:\.|$)/,'');if (d.length==0) break;try{document.domain=d;}catch (e){break;}}})();");
            System.Web.HttpContext.Current.Response.Write("window.parent.OnUploadCompleted(" + isSucceed.ToString().ToLower() + ", '" + fileUrl + "', '" + customMsg + "');");
            System.Web.HttpContext.Current.Response.Write("</script>");
            System.Web.HttpContext.Current.Response.End();
        }

        
/// <summary>
        
/// 图片文件处理及保存
        
/// 无论结果如何,类的逻辑终点处必需调用 SendFileUploadResponse 以响应FCK客户端
        
/// </summary>
        public abstract void Save();

    }//end class
}

最后是自定义上传处理类,里面可以根据自己情况添加任何逻辑,但它必须继承于FredCK.FCKeditorV2.ImageUpload.Base,并且在处理终点调用SendFileUploadResponse方法,以告之Fck客户端进行后继处理,如下代码示例了对上传图片大小、文件类型验证、文件保存:

ContractedBlock.gif ExpandedBlockStart.gif 代码
using  System;
using  System.Web;

namespace  FCKEditorSimpleNet.Web.MyImageUploadHandler
{
    
public   class  Default : FredCK.FCKeditorV2.ImageUpload.Base
    {
        
///   <summary>
        
///  允许的上传图片大小(KB)
        
///   </summary>
         private   const   int  FILE_MAX  =   1024 ;

        
public   override   void  Save()
        {
            
if  ( this .PostFile  !=   null )
            {
                
if  ( this .PostFile.ContentLength  >   0 )
                {
                    
if  ( this .PostFile.ContentLength  <=  FILE_MAX  *   1024 )
                    {
                        
if  ( this .PostFile.ContentType  ==   " image/pjpeg "   ||   this .PostFile.ContentType  ==   " image/jpeg "   ||   this .PostFile.ContentType  ==   " image/gif "   ||   this .PostFile.ContentType  ==   " image/bmp "   ||   this .PostFile.ContentType  ==   " image/png " )
                        {
                            
// 图片处理扩展

                            
// 保存
                             string  fileName  =  Guid.NewGuid().ToString()  +   " .jpg " ;
                            
string  path  =  HttpRuntime.AppDomainAppPath  +   @" fck_upload\ " ;
                            
this .PostFile.SaveAs(path  +  fileName);

                            
// 客户端响应
                            FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse( true " /fck_upload/ "   +  fileName,  " 图片上传成功! " );
                        }
                        
else
                        {
                            FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse( false "" " 图片格式不正确!目前支持JPG、GIF、BMP与PNG格式 " );
                        }
                    }
                    
else
                    {
                        FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse( false "" " 图处大小不能超过 "   +  FILE_MAX.ToString()  +   " KB! " );
                    }
                }
                
else
                {
                    FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse( false "" " 未获取图片数据! " );
                }
            }
            
else
            {
                FredCK.FCKeditorV2.ImageUpload.Base.SendFileUploadResponse( false "" " 未获取图片对象! " );
            }
        }

    } // end class
}


结束语

从学习的角度对FCKEditor原代码进行分析和作了部分修改,因FCKEditor是一个复杂的基于JS的开源软件,更多细节难以在文章中描述,可下载查看DEMO深入了解,希望对有需要的朋友带来帮助。同时因个人能力、时间、精力有限,不足之处还请指正,后继也会进一步完善本文,包括FCKEditor的JS框架、配置、Dialog实现、JS拖拽实现、窗体关系、多语言实现......每个细节几乎都能作为一个独立项来研究,之后会逐渐补充完整。

 

DEMO下载

点击下载FCKEditorSimpleNet 2.65

转自:http://www.cnblogs.com/luck0235/archive/2010/01/08/1642298.html

相关资料:http://www.cnblogs.com/cgli/archive/2011/04/28/2031713.html

转载于:https://www.cnblogs.com/jaywoo/archive/2011/06/14/2081033.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值