在WEB API專案中使用KindEditor富文字編輯器
需求大概是這個樣子(專案架構為WEB API+WEB端+Winform端+Android端):
WEB端和Winform端使用KindEditor來編輯和檢視文件內容,文件內容儲存到資料庫,插入的圖片儲存到API伺服器。儲存和上傳圖片的動作整合到
API中。Android端可以檢視文件內容,不過不是我負責就不管了。
先研究API。有中文文件看起來很舒服,不過配套的DEMO太少,文件也不夠詳細。
http://kindeditor.net/doc.php
再找DEMO。
http://www.cnblogs.com/shaoming01/archive/2012/02/03/winformhtmleditor.html
然後開始往自己的專案裡面加東西。
先把下載的壓縮包解壓加到專案裡面,最好放在根目錄,這樣加引用的時候省事一點。
API:新增一個新控制器,用來上傳圖片。(其實就是把官方DEMO裡面的upload_json.ashx拿過來)
程式碼裡面高亮了一句,這個URL一定要是形如http://abc.com/123.jpg這樣可以直接訪問的URL,否則編輯器沒法讀取。
然後伺服器上自己建立一下資料夾。
WEB端:把官方的demo.aspx改一改就能用了。using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using System.Collections; using System.Configuration; using System.Web; using System.IO; using System.Globalization; using LitJson; namespace DocManagerAPI.Controllers { public class DocImageController : ApiController { [FormAuth] [AcceptVerbs("GET", "POST")] [WebApiTracker] public void upLoadDocImage() {//上傳文件內容中的檔案 String aspxUrl = HttpContext.Current.Request.Path.Substring(0, HttpContext.Current.Request.Path.LastIndexOf("/") + 1); //檔案儲存目錄路徑 String savePath = "/DocImage/"; //檔案儲存目錄URL String saveUrl = System.Configuration.ConfigurationManager.AppSettings["serverUrl"] + "DocImage/";
//定義允許上傳的副檔名 Hashtable extTable = new Hashtable(); extTable.Add("image", "gif,jpg,jpeg,png,bmp"); //extTable.Add("flash", "swf,flv"); //extTable.Add("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb"); //extTable.Add("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2"); //最大檔案大小 int maxSize = 1000000; //this.ControllerContext =HttpContext.Current.Session; HttpPostedFile imgFile = HttpContext.Current.Request.Files["imgFile"]; if (imgFile == null) { showError("請選擇檔案。"); } String dirPath = HttpContext.Current.Server.MapPath(savePath); if (!Directory.Exists(dirPath)) { showError("上傳目錄不存在。"); } String dirName = HttpContext.Current.Request.QueryString["dir"]; if (String.IsNullOrEmpty(dirName)) { dirName = "image"; } if (!extTable.ContainsKey(dirName)) { showError("目錄名不正確。"); } String fileName = imgFile.FileName; String fileExt = Path.GetExtension(fileName).ToLower(); if (imgFile.InputStream == null || imgFile.InputStream.Length > maxSize) { showError("上傳檔案大小超過限制。"); } if (String.IsNullOrEmpty(fileExt) || Array.IndexOf(((String)extTable[dirName]).Split(','), fileExt.Substring(1).ToLower()) == -1) { showError("上傳副檔名是不允許的副檔名。\n只允許" + ((String)extTable[dirName]) + "格式。"); } //建立資料夾 dirPath += dirName + "/"; saveUrl += dirName + "/"; if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } String ymd = DateTime.Now.ToString("yyyyMMdd", DateTimeFormatInfo.InvariantInfo); dirPath += ymd + "/"; saveUrl += ymd + "/"; if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } String newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + fileExt; String filePath = dirPath + newFileName; imgFile.SaveAs(filePath); String fileUrl = saveUrl + newFileName; Hashtable hash = new Hashtable(); hash["error"] = 0; hash["url"] = fileUrl; HttpContext.Current.Response.AddHeader("Content-Type", "text/html; charset=UTF-8"); HttpContext.Current.Response.Write(JsonMapper.ToJson(hash)); HttpContext.Current.Response.End(); } private void showError(string message) { Hashtable hash = new Hashtable(); hash["error"] = 1; hash["message"] = message; HttpContext.Current.Response.AddHeader("Content-Type", "text/html; charset=UTF-8"); HttpContext.Current.Response.Write(JsonMapper.ToJson(hash)); HttpContext.Current.Response.End(); } } }
重點關注的幾個地方:
ValidateRequest="false"
不加會報錯。
var s = System.Web.HttpContext.Current.Request.Cookies;
這一段是身份驗證用的。因為上傳圖片的API需要驗證身份(我的API裡是要求提供SessionID的cookie),在WEB專案中請求這個介面是沒問題的,系統會自動把Cookie帶過去,用Winform請求的話就會出現身份驗證失敗的問題,所以要加上這麼一段。
function bindData(data)
初始化頁面的時候載入資料用。
uploadJson: '/api/DocImage/upLoadDocImage',//處理上傳圖片的程式路徑
這個引數指定處理上傳圖片請求的程式路徑,就是之前API的路徑。textarea id="content1"
記下這個ID,之後會用到。<%@ Page Language="C#" AutoEventWireup="true" ValidateRequest="false" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
var s = System.Web.HttpContext.Current.Request.Cookies;
if (s.Count > 0)
Response.Cookies.Add(s[0]);//設定Cookie為客戶端發來的Cookie
}
</script>
<!doctype html>
<html>
<head runat="server">
<meta charset="utf-8" />
<title>KindEditor ASP.NET</title>
<link rel="stylesheet" href="../themes/default/default.css" />
<link rel="stylesheet" href="../plugins/code/prettify.css" />
<script charset="utf-8" src="../kindeditor.js"></script>
<script charset="utf-8" src="../lang/zh-CN.js"></script>
<script type="text/javascript" src="../../jquery/jquery.min.js"></script>
<script>
var editor;
function bindData(data)
{
editor.html(data);
}
$(document).ready(function () {
var user = '<%=Session["user"] %>';
if (user == '') {
alert('登入無效,請重新登入!');
window.location.href = "about:blank";
}
else
{
var options = {
uploadJson: '/api/DocImage/upLoadDocImage',//處理上傳圖片的程式路徑
allowFileManager: false,//允許檢視遠端圖片
fullscreenMode: true,//全屏
afterBlur: function () {
this.sync();
},
afterCreate: function () {
this.sync();
}
}
KindEditor.ready(function (K) {
editor = K.create('#content1', options);
});
}
})
</script>
</head>
<body>
<form id="example" runat="server">
<textarea id="content1" cols="100" rows="8" style="width: 700px; height: 200px; visibility: hidden;" runat="server"></textarea>
<br />
</form>
</body>
</html>
因為WEB端已經有了管理文件的介面,所以在之前的頁面中用iframe載入這個aspx。
<iframe id="iframepage" onload="iFrameHeight()" class="iframe"></iframe>
設定iframe的大小:
function iFrameHeight() {
//文件內容 大小自適應
var ifm = document.getElementById("iframepage");
var subWeb = document.frames ? document.frames["iframepage"].document : ifm.contentDocument;
if (ifm != null && subWeb != null) {
ifm.height = subWeb.body.scrollHeight;
ifm.width = subWeb.body.scrollWidth;
}
}
初始化頁面:
(doctext是API中獲取到的文件內容,由於是公司專案就不貼全部程式碼了)
if ($('#iframepage').attr('src') == null)
{
$('#iframepage').attr('src', '../KindEditor/asp.net/demo.aspx');
document.getElementById('iframepage').onload = function () {
if (doctext != null)//第一次載入頁面是在載入完成後重新整理資料
document.getElementById("iframepage").contentWindow.bindData(doctext);
else
document.getElementById("iframepage").contentWindow.bindData('');
};
}
else
{//如果頁面已經載入則重新整理資料
if (doctext != null)
document.getElementById("iframepage").contentWindow.bindData(doctext);
else
document.getElementById("iframepage").contentWindow.bindData('');
}
WEB端的初始化分兩種,一種是第一次開啟,要指定iframe的src屬性從而初始化頁面,因為載入頁面要花點時間,所以在onload事件裡面寫資料。第二種是頁面已經載入了,直接寫資料。
獲取資料:
function getKindValue()
{//獲取iframe中kindeditor控制元件的值
var ofrm1 = document.getElementById("iframepage").document;
if (ofrm1==undefined)
{
ofrm1 = document.getElementById("iframepage").contentWindow.document;
var ff = ofrm1.getElementById("content1").value;
return ff;
}
else
{
var ie = document.frames["iframepage"].document.getElementById("content1").value;
return ie;
}
}
WEB端基本就這些,儲存文件的時候把編輯器裡獲取到的字串存到資料庫裡就行了,展示的時候再取出來。
下面是Winform端。
首先拖一個WebBrowser到窗體上,然後設定以下引數:
窗體的class上面加上
[ComVisible(true)]
否則會報錯。 [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool InternetSetCookie(string lpszUrlName, string lbszCookieName, string lpszCookieData);//設定webbrowser的cookie
因為要做身份驗證,所以要使用這個方法設定cookie。
InternetSetCookie(url, "ASP.NET_SessionId", PublicCode.userinfo.cookie.Substring(25, 24));//設定cookie
webBrowser1.Navigate(url);
url是web專案中那個demo.aspx的url。這樣在winform專案裡面就不需要引用kindeditor的檔案了。
if(doc.F_DocText!="")
{//等待頁面載入完成後繫結文件內容
//因為這個操作比較耗時,所以只在確實有內容的時候進行
while (true)
{
Thread.Sleep(50);
Application.DoEvents();
if (webBrowser1.Document.GetElementById("content1") != null)
{
object[] objs = new object[1];
objs[0] = doc.F_DocText;
webBrowser1.Document.InvokeScript("bindData", objs);
break;
}
}
}
上面的程式碼是載入資料用的,因為webbrowser自帶的DocumentCompleted事件比較坑(觸發這個事件的時候頁面其實沒有載入完,多半還在執行JS),所以只能用這個比較笨的辦法,迴圈判斷控制元件是否已經初始化。程式碼中的doc.F_DocText是文件內容。
取值用下面的程式碼:
webBrowser1.Document.GetElementById("content1").GetAttribute("value");//從webbrowser獲取文件內容
主要程式碼大概就這些。
在實現功能的時候發現KindEditor有以下相容性問題:
1:在webBrowser控制元件中無法貼上剪下板中的圖片(這個應該是webBrowser的問題,但是我改了核心版本沒效果,不知道怎麼解決)
2:在IE(包括webBrowser)中無法顯示網路圖片,看url是沒問題的,但是圖片不顯示,火狐下面正常。