以前公司購買過eWebEditor,功能應該還是不錯的,但即便到了現在,也還僅是一個IE only的版本,無法滿足現在差異化的需求。故前段時間下了最新的FCKeditor2.3.3版本下來(當然了,連帶java的integration),demo來看看,發現有幾個地方非常不爽:
1、上載的文件,只能放在URL可及的地方(如默認只能放到嵌入應用路徑的/UserFiles/下);
2、沒有明確的上載視頻的按鈕;
3、圖片、FLASH、附件上載等,步驟多,復雜度高(想想,用戶不都是高手)。
怎么辦呢,改!
一、第一個就是增加一個FileLocatorServlet,思路很簡單:通過這個服務來定位文件,而不是之間產生鏈接,既是安全的考慮,也是應用集群的一個重要考慮點。而且原來的幾個servlet的配置羅嗦且重疊,難以讓人產生美感。所謂代碼勝千言,通過下面的web.xml大家應該可以看出修理的要點:
??1
<?
xml?version="1.0"?encoding="ISO-8859-1"
?>
??2
??3
<!
DOCTYPE?web-app
??4
??PUBLIC?"-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.2//EN"
??5
??"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"
>
??6
??7
<
web-app
>
??8
??
<
display-name
>
FCKeditor?Test?Application
</
display-name
>
??
??9
????
<
context-param
>
?10
????????
<!--
?setting?the?FCKecitor?context?based?parameters?
-->
?11
????????
<!--
?baseDir?means?the?root?of?the?uploaded?file/image/flash?stored?
?12
?????????????the?prefix?of?'file:/'?means?strore?in?a?file?system?root?that?cannot?get?from?webapp?url
?13
????????
-->
?14
????????
<
param-name
>
baseDir
</
param-name
>
?15
????????
<
param-value
>
file:/C:/Temp/FCKeditorUpload/
</
param-value
>
?16
????
</
context-param
>
?
?17
?18
????
<
context-param
>
?19
????????
<!--
?
?20
?????????????if?the?baseDir?prefix?by?'file:/',please?set?it.
?21
????????
-->
?22
????????
<
param-name
>
fileLocator
</
param-name
>
?23
????????
<
param-value
>
/editor/filemanager/browser/default/service/jsp/filelocator
</
param-value
>
?24
????
</
context-param
>
?
?25
?26
????
<
context-param
>
?27
????????
<!--
?
?28
?????????????debug?setting,true?means?verbose?output?to?the?console.
?29
????????
-->
?30
????????
<
param-name
>
debug
</
param-name
>
?31
????????
<
param-value
>
true
</
param-value
>
?32
????
</
context-param
>
?
?33
?34
????
<
context-param
>
?35
????????
<!--
?
?36
?????????????enabled?setting,true?means?upload?enabled.
?37
????????
-->
?38
????????
<
param-name
>
enabled
</
param-name
>
?39
????????
<
param-value
>
true
</
param-value
>
?40
????
</
context-param
>
?
?41
?42
????
<
context-param
>
?43
????????
<!--
?
?44
?????????????encoding,the?response?encoding?of?the?file/image/flash,default?is?UTF-8
?45
????????
-->
?46
????????
<
param-name
>
encoding
</
param-name
>
?47
????????
<
param-value
>
UTF-8
</
param-value
>
?48
????
</
context-param
>
?
?49
?50
????
<
context-param
>
?51
????????
<!--
?
?52
?????????????contentTypeMapping,a?map?for?the?response?ContentType
?53
????????
-->
?54
????????
<
param-name
>
contentTypeMapping
</
param-name
>
?55
????????
<
param-value
>
doc=application/vnd.ms-word
?56
????????|xls=application/vnd.ms-excel
?57
????????|jpg=image/jpeg
?58
????????|gif=image/gif
?59
????????|swf=application/x-shockwave-flash
?60
????????|avi=video/x-msvideo
?61
????????
</
param-value
>
?62
????
</
context-param
>
?
?63
?64
????
<
context-param
>
?65
????????
<!--
?
?66
?????????????allowedExtensionsFile,the?logic?is?'Not?allowed?means?deny.'
?67
????????
-->
?68
????????
<
param-name
>
allowedExtensionsFile
</
param-name
>
?69
????????
<
param-value
>
doc|xls|pdf|avi
</
param-value
>
?70
????
</
context-param
>
?
?71
?72
????
<
context-param
>
?73
????????
<!--
?
?74
?????????????allowedExtensionsImage,the?logic?is?'Not?allowed?means?deny.'
?75
????????
-->
?76
????????
<
param-name
>
allowedExtensionsImage
</
param-name
>
?77
????????
<
param-value
>
jpg|gif|png
</
param-value
>
?78
????
</
context-param
>
?
?79
?80
????
<
context-param
>
?81
????????
<!--
?
?82
?????????????allowedExtensionsFlash,the?logic?is?'Not?allowed?means?deny.'
?83
????????
-->
?84
????????
<
param-name
>
allowedExtensionsFlash
</
param-name
>
?85
????????
<
param-value
>
swf|fla
</
param-value
>
?86
????
</
context-param
>
?87
?88
????
<
servlet
>
?89
????????
<
servlet-name
>
Connector
</
servlet-name
>
?90
????????
<
servlet-class
>
com.fredck.FCKeditor.connector.ConnectorServlet
</
servlet-class
>
?91
????????
<
load-on-startup
>
1
</
load-on-startup
>
?92
????
</
servlet
>
?93
????
?94
????
<
servlet
>
?95
????????
<
servlet-name
>
FileLocator
</
servlet-name
>
?96
????????
<
servlet-class
>
com.fredck.FCKeditor.service.FileLocatorServlet
</
servlet-class
>
?97
????????
<
load-on-startup
>
1
</
load-on-startup
>
?98
????
</
servlet
>
?99
100
????
<
servlet
>
101
????????
<
servlet-name
>
SimpleUploader
</
servlet-name
>
102
????????
<
servlet-class
>
com.fredck.FCKeditor.uploader.SimpleUploaderServlet
</
servlet-class
>
103
????????
<
load-on-startup
>
1
</
load-on-startup
>
104
????
</
servlet
>
105
106
??
<
servlet-mapping
>
107
????
<
servlet-name
>
Connector
</
servlet-name
>
108
????
<
url-pattern
>
/editor/filemanager/browser/default/connectors/jsp/connector
</
url-pattern
>
109
??
</
servlet-mapping
>
110
??
111
??
<
servlet-mapping
>
112
????
<
servlet-name
>
SimpleUploader
</
servlet-name
>
113
????
<
url-pattern
>
/editor/filemanager/upload/simpleuploader
</
url-pattern
>
114
??
</
servlet-mapping
>
??
115
??
116
??
<
servlet-mapping
>
117
????
<
servlet-name
>
FileLocator
</
servlet-name
>
118
????
<
url-pattern
>
/editor/filemanager/browser/default/service/jsp/filelocator
</
url-pattern
>
119
??
</
servlet-mapping
>
??
120
121
</
web-app
>
連帶FCKeditorConfigurations.java一并修理,配置統一且singleton。關鍵代碼為:
?1
?2
????
/**?*/
/**
?3
?????*?Make?the?configuration?sigleton
?4
?????*?
@param
?sc
?5
?????*?
@return
?the?static?configuration?map
?6
?????
*/
?7
????
public
?
static
?Map?getContextConfigurationsInstance(ServletContext?sc)
{
?8
????????
if
(contextConfigurations
==
null
)
{
?9
????????????initContextConfigurations(sc);
10
????????}
11
????????
return
?contextConfigurations;
12
????}
13
????
14
????
/**?*/
/**
15
?????*?Init?all?the?FCKeditor?configuration.
16
?????*?add?by?zhengxq
17
?????*?
@param
?sc
18
?????
*/
19
????
private
?
static
?
void
?initContextConfigurations(ServletContext?sc)
{
20
????????
if
?(debug)
21
????????????System.out.println(
"
\r\n----?FCKeditorConfigurations?for?java?initialization?started?----
"
);
22
????????
23
????????String?baseDir?
=
?sc.getInitParameter(
"
baseDir
"
);
24
????????String?fileLocator?
=
?sc.getInitParameter(
"
fileLocator
"
);
25
????????String?debugStr?
=
?sc.getInitParameter(
"
debug
"
);
26
????????String?enabledStr?
=
?sc.getInitParameter(
"
enabled
"
);????????
27
????????String?encoding?
=
?sc.getInitParameter(
"
encoding
"
);
28
????????String?contentTypeMapping?
=
?sc.getInitParameter(
"
contentTypeMapping
"
);
29
????????String?AllowedExtensionsFile?
=
?sc.getInitParameter(
"
allowedExtensionsFile
"
);
30
????????String?AllowedExtensionsImage?
=
?sc.getInitParameter(
"
allowedExtensionsImage
"
);
31
????????String?AllowedExtensionsFlash?
=
?sc.getInitParameter(
"
allowedExtensionsFlash
"
);
32
????????
33
????????debug?
=
?(
new
?Boolean(debugStr)).booleanValue();
34
????????encoding?
=
?(encoding
==
null
?
||
?encoding.length()
==
0
)
?
"
UTF-8
"
:encoding;
35
????????
36
????????
if
(baseDir
==
null
?
||
?baseDir.length()
==
0
)?baseDir?
=
?defaultBaseDir;
37
????????String?realBaseDir?
=
?defaultBaseDir;
38
????????
if
?(baseDir.startsWith(fileSystemUriPrefix))?
{
39
????????????realBaseDir?
=
?baseDir.substring(fileSystemUriPrefix.length());????????????
40
????????}
?
else
?
{
41
????????????realBaseDir?
=
?sc.getRealPath(baseDir);
42
????????????fileLocator?
=
?
null
;
//
no?use?and?should?set?null
43
????????}
44
????????File?baseFile
=
new
?File(realBaseDir);
45
????????
if
(
!
baseFile.exists())
{
46
????????????baseFile.mkdir();
47
????????}
48
????????contextConfigurations?
=
?
new
?HashMap();
49
????????contextConfigurations.put(
"
baseDir
"
,baseDir);
50
????????contextConfigurations.put(
"
realBaseDir
"
,realBaseDir);
51
????????contextConfigurations.put(
"
fileLocator
"
,fileLocator);
52
????????contextConfigurations.put(
"
debug
"
,debugStr);
53
????????contextConfigurations.put(
"
enabled
"
,enabledStr);
54
????????contextConfigurations.put(
"
encoding
"
,encoding);
55
????????contextConfigurations.put(
"
contentTypeMapping
"
,contentTypeMappingToMap(contentTypeMapping));
56
????????contextConfigurations.put(
"
allowedExtensionsFile
"
,stringToArrayList(AllowedExtensionsFile));
57
????????contextConfigurations.put(
"
allowedExtensionsImage
"
,stringToArrayList(AllowedExtensionsImage));
58
????????contextConfigurations.put(
"
allowedExtensionsFlash
"
,stringToArrayList(AllowedExtensionsFlash));
59
????????
60
????????
if
?(debug)
61
????????????System.out.println(
"
\r\n----?FCKeditorConfigurations?for?java?initialization?end?----
"
);
62
????????
63
????}
FileLocatorServlet.java也很簡單,無非就是文件的物理定位和文件流的輸出:
?1
String?type?
=
?request.getParameter(
"
Type
"
);
?2
????????String?fileName?
=
?request.getParameter(
"
FileName
"
);
?3
?4
????????String?realFilePath?
=
?config.get(
"
realBaseDir
"
)?
+
?type?
+
?
"
/
"
?
+
?fileName;
?5
????????File?file?
=
?
new
?File(realFilePath);
?6
????????
if
?(file.exists())?
{
?7
????????????response.setHeader(
"
Content-Transfer-Encoding
"
,?
"
base64
"
);
?8
????????????response.setHeader(
"
Cache-Control
"
,?
"
no-store
"
);
?9
????????????response.setHeader(
"
Pragma
"
,?
"
no-cache
"
);
10
????????????response.setDateHeader(
"
Expires
"
,?
0
);
11
????????????response.setContentType(getContentTypeByFileExt(fileName.substring(fileName.lastIndexOf(
"
.
"
))));
12
????????????
13
????????????ServletOutputStream?out?
=
?response.getOutputStream();????????????
14
????????????InputStream?in?
=
?
new
?FileInputStream(file);
15
????????????BufferedInputStream?bis?
=
?
new
?BufferedInputStream(in);
16
????????????BufferedOutputStream?bos?
=
?
new
?BufferedOutputStream(out);
17
????????????
byte
[]?buff?
=
?
new
?
byte
[
2048
];
18
????????????
int
?bytesRead;
19
????????????
while
?(
-
1
?
!=
?(bytesRead?
=
?bis.read(buff,?
0
,?buff.length)))?
{
20
????????????????bos.write(buff,?
0
,?bytesRead);
21
????????????}
22
????????????
if
?(bis?
!=
?
null
)?
{
23
????????????????bis.close();
24
????????????}
25
????????????
if
?(bos?
!=
?
null
)?
{
26
????????????????bos.close();
27
????????????}
28
????????}
?
else
?
{
29
????????????
throw
?
new
?FileNotFoundException(fileName);
30
????????}
上述改動已經提交給了FCKeditor,如果大家真的有興趣,可以去找里面我所提交的patch。
二、至于上述的2、3問題,同樣,動手即可解決,在此略過。
過程中倒是碰到幾個有意思的問題,成了花絮,其實也是使用FCKeditor的一些心得,寫寫可能還有點意思:
1、如何取得FCKeditor的值?
答案:這是我們常常干的事情:取得這個值并賦值給某個hidden,再合法性檢查+submit等。怎么取得呢?這樣:
1
????
var
?oEditor?
=
?FCKeditorAPI.GetInstance('editor')?;
2
????
//
Get?the?editor?contents?in?XHTML.
3
????
//
alert(?oEditor.GetXHTML(true)?)?;????//?"true"?means?you?want?it?formatted.
4
????document.all(
"
tip.c_content
"
).value
=
oEditor.GetXHTML(
true
);
2、如何使得FCKeditor接收tab鍵?
答案:我們希望界面元素按照外面的安排進行tab切換,但FCKeditor怎么能做到呢?也有辦法:
1
function
?focusIframeOnTab(caller,?tabTargetId,?callEvent)
{
2
????????????????
//
?If?keypress?TAB?and?not?SHIFT+TAB?
3
????????????????
if
(callEvent.keyCode?
==
?
9
?
&&
?
!
callEvent.shiftKey)
4
????????????????????document.getElementById(tabTargetId).contentWindow.focus();
5
????????????}
光光有個函數頂個什么用,還要這樣:在之前的那個界面元素中加上下面的事件,如使用struts的tag的化,這樣就可以了:
<
html:text?
property
="tip.c_title"
?style
="width:450px"
?tabindex
="1"
?onkeydown
="focusIframeOnTab(this,?'editor___Frame',event);if(!document.all)?return?false;"
/>
這點是google了半天最終在FCKeditor的FAQ中找到的,看來以后用開源的軟件第一件事情就是看FAQ,錯不了!
3、如何希望在FCKeditor加載完畢后做點什么事情?
答案:也很簡單,編寫自己的FCKeditor_OnComplete函數,如:
function
?FCKeditor_OnComplete(?editorInstance?)?
{
????????????????window.status?
=
?editorInstance.Description?;
????????????}
4、如果在圖片、FLASH等界面中上載了東西后,希望能告訴自己的表單,怎么做?
答案:這個花了我不少看代碼和調試時間!其實這里的關鍵就是如何獲取嵌入FCKeditor的那個window,這樣就可以了,在對應的js文件(如editor\dialog\fck_image\fck_image.js)中的ok方法的最后加入:
????
//
edit?by?zhengxq
????
try
{????????
????????
var
?obj?
=
?window.dialogArguments.Editor.parent.document;
????????obj.getElementById(
"
tip.c_tip_has_pic
"
).value?
=
?
"
1
"
;

????}
catch
(e)
{}
????
關鍵就是:window.dialogArguments.Editor.parent.document,這個能夠找到對應窗口的引用,有了這個,還不會控制嗎?!