1. 程式人生 > >在WEB API專案中使用KindEditor富文字編輯器

在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,否則編輯器沒法讀取。

然後伺服器上自己建立一下資料夾。

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(); } } }
WEB端:把官方的demo.aspx改一改就能用了。
重點關注的幾個地方:
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是沒問題的,但是圖片不顯示,火狐下面正常。