替百度文本编辑器UEditor添加附件管理功能
为百度文本编辑器UEditor添加附件管理功能
最近朋友的一个项目需要一个文本编辑器,让我突然想起那个百度编辑器UEditor,功能比较齐全而且开源,但也有很多bug和不足。
http://ueditor.baidu.com/website/onlinedemo.html
一年前我就就曾经关注过,让我感到最不能容忍的不足就是,添加的附件的地方,文件和服务器端重名这样的错误如果能够容忍,但如果上传错了,我想删除怎么办呢,居然没有附件管理,如下图所示,字体也太小:
为此我优化了上传,主要是增加增了附件管理,可以删除不需要的附件,效果如下:
上传文件增加了文件类型,和重名判断
附件管理部分,不仅可以显示服务器端的不同附件,而且双击附件可以删除附件。
首先在ueditor\server\submit\net目录下,这里以.net为例,添加一个服务器端文件fileManager.ashx,用来显示服务器端的附件内容,以及删除附件操作代码,前端页面通过AJAX来调用这个文件。
<%@ WebHandler Language="C#" Class="imageManager" %> /** * User: xuhk * Date: 13-3-15 * 对已经上传的附件进行管理,显示附件,删除附件 */ using System; using System.Web; using System.IO; public class imageManager : IHttpHandler { public void ProcessRequest (HttpContext context) { context.Response.ContentType = "text/plain"; string path = context.Server.MapPath("../../upload/uploadfiles"); string[] filetype = { ".rar", ".doc", ".docx", ".zip", ".pdf", ".txt", ".swf", ".wmv" }; //文件允许格式 string action = context.Server.HtmlEncode(context.Request["action"]); if(action == "get") { String str=String.Empty; DirectoryInfo info = new DirectoryInfo(path); //目录验证 if (info.Exists) { foreach (FileInfo fi in info.GetFiles()) { if (Array.IndexOf(filetype, fi.Extension) != -1) { str += "uploadfiles/" + fi.Name + "ue_separate_ue"; } } } context.Response.Write(str); } //删除选中的文件 string fileName = context.Server.HtmlEncode(context.Request["fileName"]); if (action == "del") { try { String fullPath = String.Empty; fullPath = path + "/" + fileName; File.Delete(fullPath); context.Response.Write("success"); } catch { context.Response.Write("error"); } } } public bool IsReusable { get { return false; } } }
目录ueditor\dialogs\attachment下的文件attachment.html改为如下形式:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>附件上传</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <link rel="stylesheet" type="text/css" href="attachment.css" /> </head> <body> <div class="wrapper"> <div id="fileTab"> <div id="tabHeads"> <span tabsrc="file" class="focus">附件上传</span> <span tabsrc="fileManager">文件管理</span> </div> <div id="tabBodys"> <div id="file" class="panel"> <div class="controller"> <span id="divStatus">本次共成功上传 0 个文件</span> <span id="spanButtonPlaceHolder"></span> </div> <div class="fieldset flash" id="fsUploadProgress"> </div> <span id="startUpload" style="display: none;"></span> </div> <div id="fileManager" style="display: none;" class="panel"> <div id="fileListView" style="display: none;"> 文件加载中……</div> <span id="fileListAddress" style="display: none;"></span> </div> </div> </div> </div> <script type="text/javascript" src="../internal.js"></script> <script type="text/javascript" src="../../third-party/swfupload/swfupload.js"></script> <script type="text/javascript" src="../../third-party/swfupload/swfupload.queue.js"></script> <script type="text/javascript" src="../../third-party/swfupload/fileprogress.js"></script> <script type="text/javascript" src="callbacks.js"></script> <script type="text/javascript" src="fileTypeMaps.js"></script> <script type="text/javascript"> var g = $G, ajax = parent.baidu.editor.ajax; /** * 找到id下具有focus类的节点并返回该节点下的某个属性 * @param id * @param returnProperty */ function findFocus(id, returnProperty) { var tabs = g(id).children, property; for (var i = 0, ci; ci = tabs[i++]; ) { if (ci.className == "focus") { property = ci.getAttribute(returnProperty); break; } } return property; } /** * 绑定确认按钮 */ function addOKListener() { dialog.onok = function() { var currentTab = findFocus("tabHeads", "tabSrc"); switch (currentTab) { case "file": return insertBatch(); break; case "fileManager": insertSearch("fileListView"); return; break; default: return; } } } //插入单个文件 function insertBatch() { var map = fileTypeMaps, str = ""; for (var i = 0, ci; ci = filesList[i++]; ) { var src = editor.options.UEDITOR_HOME_URL + "dialogs/attachment/fileTypeImages/" + (map[ci.type] || "icon_default.png"); str += "<p style='line-height: 16px;'><img src='" + src + "' data_ue_src='" + src + "' />" + "<a href='" + editor.options.filePath + ci.url + "'>" + ci.title + "</a></p>"; } editor.execCommand("insertHTML", str); swfupload.destroy(); } //插入多个文件 function insertSearch(id) { var fileTypeImg = $G("fileListView").getElementsByTagName("img"); var fileAddress = $G("fileListAddress").getElementsByTagName("a"); for (var i = 0, ci; ci = fileTypeImg[i++]; ) { if (ci.getAttribute("selected")) { //i被加1了,所以这里减一 var ci2 = fileAddress[i - 1]; //地址 var url = ci2.getAttribute("src").replace(/(\s*$)/g, ""); //文件名 var arrTitle = url.split("/"); var strTitle = arrTitle[arrTitle.length - 1]; //文件类型 var arrType = url.split("."); var strType = "." + arrType[arrType.length - 1]; filesList.push({ title: strTitle, url: url, type: strType }); } } insertBatch() } /** * 延迟加载 */ function addScrollListener() { g("fileListView").onscroll = function() { var fileTypeImg = this.getElementsByTagName("img"), top = Math.ceil(this.scrollTop / 100) - 1; top = top < 0 ? 0 : top; for (var i = top * 5; i < (top + 5) * 5; i++) { var img = fileTypeImg[i]; if (img && !img.getAttribute("src")) { img.src = img.getAttribute("lazy_src"); img.removeAttribute("lazy_src"); } } } } /** * 附件缩放 * @param img * @param max */ function scale(img, max, oWidth, oHeight) { var width = 0, height = 0, percent, ow = img.width || oWidth, oh = img.height || oHeight; if (ow > max || oh > max) { if (ow >= oh) { if (width = ow - max) { percent = (width / ow).toFixed(2); img.height = oh - oh * percent; img.width = max; } } else { if (height = oh - max) { percent = (height / oh).toFixed(2); img.width = ow - ow * percent; img.height = max; } } } } function selectTxt(node) { if (node.select) { node.select(); } else { var r = node.createTextRange && node.createTextRange(); r.select(); } } /** * 改变o的选中状态 * @param o */ function changeSelected(o) { if (o.getAttribute("selected")) { o.removeAttribute("selected"); o.childNodes[0].removeAttribute("selected"); o.style.cssText = "filter:alpha(Opacity=100);-moz-opacity:1;opacity: 1;border: 1px solid #E8E8E8;"; /*为选中文件下部的文件名DIV部分增加样式*/ o.childNodes[1].style.cssText = "filter:alpha(Opacity=100);-moz-opacity:1;opacity: 1;border: 0px solid #E8E8E8;"; } else { o.setAttribute("selected", "true"); o.childNodes[0].setAttribute("selected", "true"); o.style.cssText = "filter:alpha(Opacity=100);-moz-opacity:0.5;opacity: 0.5;border:1px solid #316ac5;background-color: #F0F5FF;"; /*为选中文件下部的文件名DIV部分增加样式*/ o.childNodes[1].style.cssText = "filter:alpha(Opacity=100);-moz-opacity:0.5;opacity: 0.5;border:0px solid #316ac5;background-color: #F0F5FF;"; } } /** * tab切换 * @param tabParentId * @param keepFocus 当此值为真时,切换按钮上会保留focus的样式 */ function switchTab(tabParentId, keepFocus) { var tabElements = $G(tabParentId).children, tabHeads = tabElements[0].children, tabBodys = tabElements[1].children; var map = fileTypeMaps; for (var i = 0, length = tabHeads.length; i < length; i++) { var head = tabHeads[i]; domUtils.on(head, "click", function() { //head样式更改 for (var k = 0, len = tabHeads.length; k < len; k++) { if (!keepFocus) tabHeads[k].className = ""; } this.className = "focus"; //body显隐 var tabSrc = this.getAttribute("tabSrc"); for (var j = 0, length = tabBodys.length; j < length; j++) { var body = tabBodys[j], id = body.getAttribute("id"); if (id == tabSrc) { body.style.display = ""; if (id == "fileManager") { //转换到文件管理TAB时,显示DIV里的内容 g("fileManager").style.display = ""; g("fileListView").style.display = ""; var list = g("fileListView"); //已经初始化过时不再重复提交请求 //2013-03-09删除这个判断,这样才能保证上传新文件后,点管理附件显示新的附件你 // if (!list.children.length) { ajax.request(editor.options.fileManagerPath, { timeout: 100000, action: "get", onsuccess: function(xhr) { var tmp = xhr.responseText, imageUrls = !tmp ? [] : tmp.split("ue_separate_ue"), length = imageUrls.length; //当服务器没上传文件的时候 if (0 == length) { g("fileManager").style.display = ""; list.style.display = ""; list.innerHTML = !length ? " 当前未上传过任何附件!" : ""; } else { list.innerHTML = ""; } for (var i = 0, ci; ci = imageUrls[i++]; ) { //文件真实地址 var url = editor.options.UEDITOR_HOME_URL + ci; //创建一个图片标签来显示文件类型图标 var img = document.createElement("img"); //创建一个链接标签来存放文件真实地址 var a = document.createElement("a"); //显示单个文件信息的DIV快 var divFileView = document.createElement("div"); //存放单个文件地址 var divFileAddress = document.createElement("div"); //在文件类型图标上,增加个fileAddress属性,存放该文件的真实地址,目的为了双击图标删除此文件 divFileView.setAttribute("fileAddress", url); divFileView.appendChild(img); // 在显示文件类型图标下显示文件名 var divFileName = document.createElement("div"); var arrFileName = ci.split("/"); var strFileName = arrFileName[arrFileName.length - 1]; //在鼠标放上面给出全文件名提示 img.parentNode.title = strFileName //获得文件扩展名前点的位置,并去掉文件扩展名 var indexDot = strFileName.lastIndexOf("."); strFileName = strFileName.substring(0, indexDot); //文件名太长就截取 if (strFileName.length >= 6) strFileName = strFileName.substring(0, 6); //把文件名显示在图标下面 divFileName.innerHTML = strFileName; divFileView.appendChild(divFileName); divFileAddress.appendChild(a); //显示文件类型 g("fileListView").appendChild(divFileView); //隐藏DIV,对应显示文件的真实地址 g("fileListAddress").appendChild(divFileAddress); //单击选中 img.parentNode.onclick = function() { changeSelected(this); }; //双击删除 img.parentNode.ondblclick = function() { var me = this, src = me.getAttribute("fileAddress"); if (!confirm("删除操作不可恢复,您确认要删除本附件么?")) return; ajax.request(editor.options.fileManagerPath, { action: "del", fileName: src.substr(src.lastIndexOf("/") + 1), onsuccess: function(xhr) { if (xhr.responseText == "success") { me.parentNode.removeChild(me); } else { alert("服务器删除附件失败,请重试!"); } } }); }; img.onload = function() { this.parentNode.style.display = ""; var w = this.width, h = this.height; scale(this, 100, 200, 80); }; //文件类型图标地址 var arrType = url.split("."); var strType = "." + arrType[arrType.length - 1]; var imgTypeURL = editor.options.UEDITOR_HOME_URL + "dialogs/attachment/fileTypeImages/" + map[strType]; img.setAttribute(i < 35 ? "src" : "lazy_src", imgTypeURL); img.setAttribute("data_ue_src", imgTypeURL); //文件存放真实地址 a.setAttribute(i < 35 ? "src" : "lazy_src", ci); a.setAttribute("data_ue_src", ci); list.style.display = ""; } }, onerror: function() { g("fileListView").innerHTML = "糟糕,附件读取失败了!"; } }); // } } //当切换到本地附件上传时,隐藏遮罩用的iframe if (id == "file") { g("fileManager").style.display = "none"; g("fileListView").style.display = "none"; } } else { body.style.display = "none"; } } }); } } var swfupload, filesList = []; window.onload = function() { var settings = { flash_url: "../../third-party/swfupload/swfupload.swf", flash9_url: "../../third-party/swfupload/swfupload_fp9.swf", upload_url: "../../server/upload/net/fileUp.ashx", //附件上传服务器地址 post_params: { "PHPSESSID": "<?php echo session_id(); ?>" }, //解决session丢失问题 file_size_limit: "100 MB", //文件大小限制,此处仅是前端flash选择时候的限制,具体还需要和后端结合判断 file_types: "*.*", //允许的扩展名,多个扩展名之间用分号隔开,支持*通配符 file_types_description: "All Files", //扩展名描述 file_upload_limit: 100, //单次可同时上传的文件数目 file_queue_limit: 10, //队列中可同时上传的文件数目 custom_settings: { //自定义设置,用户可在此向服务器传递自定义变量 progressTarget: "fsUploadProgress", startUploadId: "startUpload" }, debug: false, // 按钮设置 button_image_url: "../../themes/default/images/fileScan.png", button_width: "100", button_height: "25", button_placeholder_id: "spanButtonPlaceHolder", button_text: '<span class="theFont">文件浏览…</span>', button_text_style: ".theFont { font-size:14px;}", button_text_left_padding: 10, button_text_top_padding: 4, // 所有回调函数 in handlers.js swfupload_preload_handler: preLoad, swfupload_load_failed_handler: loadFailed, file_queued_handler: fileQueued, file_queue_error_handler: fileQueueError, //选择文件完成回调 file_dialog_complete_handler: function(numFilesSelected, numFilesQueued) { var me = this; //此处的this是swfupload对象 if (numFilesQueued > 0) { dialog.buttons[0].setDisabled(true); var start = $G(this.customSettings.startUploadId); start.style.display = ""; start.onclick = function() { me.startUpload(); start.style.display = "none"; } } }, upload_start_handler: uploadStart, upload_progress_handler: uploadProgress, upload_error_handler: uploadError, upload_success_handler: function(file, serverData) { try { var info = eval("(" + serverData + ")"); } catch (e) { } var progress = new FileProgress(file, this.customSettings.progressTarget); if (info.state == "SUCCESS") { progress.setComplete(); progress.setStatus("<span style='color: #0b0;font-weight: bold'>上传成功!</span>"); //filesList.push({ url: info.url, type: info.fileType}); filesList.push({ title: info.title, url: info.url, type: info.fileType }); progress.toggleCancel(true, this, "从成功队列中移除"); } else { progress.setError(); progress.setStatus(info.state); progress.toggleCancel(true, this, "移除保存失败文件"); } }, //上传完成回调 upload_complete_handler: uploadComplete, //队列完成回调 queue_complete_handler: function(numFilesUploaded) { dialog.buttons[0].setDisabled(false); var status = $G("divStatus"); var num = status.innerHTML.match(/\d+/g); status.innerHTML = "本次共成功上传 " + ((num && num[0] ? parseInt(num[0]) : 0) + numFilesUploaded) + " 个文件"; } }; swfupload = new SWFUpload(settings); dialog.oncancel = function() { swfupload.destroy(); } switchTab("fileTab"); addOKListener(); addScrollListener(); }; </script> </body> </html>