在线富文本编辑器,带下载为 Word 选项






4.96/5 (31投票s)
在线富文本编辑器,带邮件合并和下载为 Word 选项
引言
本文介绍如何创建具有以下功能的在线富文本编辑器:
- 在线富文本编辑
- 保存富文本
- 下载为Word文档
- 邮件合并功能
背景
我使用Google Doc已经有一段时间了。我一直在想,是否能利用我已有的编程知识创建类似的东西。在网上搜索后,我没有找到任何(开源的)能提供类似Google功能和邮件合并的工具。我不想从头开始编写富文本编辑器。在网上搜索后,我发现FCKEditor应该能满足我的需求。此外,微软的OpenXML SDK可用于邮件合并和转换为docx文件。
您需要FCKEditor作为在线编辑器。(您可以在此处下载。)
您可以在此处下载OpenXML SDK。)
Using the Code
项目设置
请参阅下面在Visual Studio中设置项目的步骤(我使用的是Visual Studio 2008)。
- 将源文件下载并解压到您选择的文件夹中
- 要简单使用,请在IIS中将此文件夹配置为虚拟文件夹(MSDN上有一个链接,解释了如何操作)
- 打开 Visual Studio
-
- 点击打开“网站”类型的项目
- 将此文件夹作为网站打开
- 添加对“
DocumentFormat.OpenXml
”和“WindowsBase
”的引用。您可以在安装OpenXML SDK 2.0时获取这些引用。或者,我已将这两个DLL文件包含在lib文件夹中。 - 运行项目
代码解释
该项目包含一个名为“scripts”的文件夹。此文件夹包含FCKEditor文件。它还包含jquery文件和我们的脚本文件“myAjax.js”(我稍后会解释它们)。您还会找到一个名为“docs”的文件夹。此文件夹将存放我们生成的Word文档。
在根文件夹中,我们有三个文件
- Default.aspx - 默认网页
- downloader.ashx - 此文件处理我们的下载逻辑
- web.config - 普通配置文件
Default.aspx
我们首先打开Default.aspx文件。在<head>
部分,包含以下JavaScript文件引用。这些文件将允许我们使用FCKEditor,并提供JQuery和我们的JavaScript文件的引用
<script src="scripts/ckeditor/ckeditor.js" type="text/javascript"></script>
<script src="scripts/ckeditor/adapters/jquery.js" type="text/javascript"></script>
<script type="text/javascript" src="scripts/jquery-1.3.2.min.js"></script>
<script src="scripts/myAjax.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
现在我们跳到HTML。稍后我们会回到head中的其他脚本。在表单上,我们简单地为页面添加一个标题
Online Richtext editor with Mail Merge
然后我们处理那些可能未启用JavaScript的浏览器。
<noscript>
Online Richtext editor requires JavaScript to run.
In a browser with no JavaScript support, like yours, you should still see
the contents (HTML data) and you should be able to edit it normally,
without a rich editor interface.
</noscript>
接下来,我们添加一些说明和一个文本区域。该文本区域将用作我们的富文本编辑器。我们使用内置的FCKEditor方法“CKEditor.Replace
”将文本区域转换为FCKEditor富文本编辑器。我们还添加了一个div
标签来显示我们的消息
<p>
This is online rich text editor sample with mail merge.<br />
Use the text area below to type in your document. <br />
Use the dropdowns below that have mail merge fields.
</p>
<textarea cols="50" id="editor1" name="editor1" rows="10"></textarea>
<!-- instantiate a new instance of CKEDITOR -->
<script type="text/javascript">
//<![CDATA[
// Replace the <textarea id="editor1"> with an CKEditor instance.
var editor = CKEDITOR.replace('editor1',
{
toolbar: 'myToolBar', skin: 'office2003', width: '60%'
});
//]]>
</script>
<div id="eMessage" />
最后一部分是添加邮件合并字段的下拉菜单和生成Word文档的按钮。如您所见,我们为按钮设置了一个onclick
处理程序。ajaxDownloadDoc()
函数在我们的JavaScript文件“myAjax.js”中。这里我们使用JQuery的AJAX方法将数据发送到我们的处理程序并生成Word文档。有关处理程序的更多信息,请参见下一节。
Insert Merge Fields from here
<select id="MergeFields" >
<option value="0" selected="selected">Select Merge Fields</option>
<option value="1">{^Title^}</option>
<option value="2">{^FirstName^}</option>
<option value="3">{^LastName^}</option>
</select>
<input type="button" name="saveAsWord" id="saveAsWord"
önclick="javascript:ajaxDownloadDoc()" value="Download as word" />
如前所述,这里是实际在富文本编辑器中插入邮件合并字段的JavaScript。我们使用JQuery来确保表单已加载。然后我们利用“MergeFields
”下拉菜单的onSelectChange
事件。在这里我们调用我们的函数并将选定的值插入到FCKEditor中。
oEditor.insertHtml(valueToInsert);
这是代码
//select the merge fields dropdown and implement the onchange event
//to insert the selected value in the text area
$(document).ready(function() {
$("#MergeFields").val('0');
$("#MergeFields").change(onSelectChange);
});
function onSelectChange() {
var selected = $("#MergeFields option:selected");
var oEditor = CKEDITOR.instances.editor1;
if (selected.val() != 0) {
var valueToInsert = selected.text();
// Check the active editing mode.
if (oEditor.mode == 'wysiwyg') {
// Insert the desired HTML.
oEditor.insertHtml(valueToInsert);
}
else {
alert('You must be on WYSIWYG mode!');
}
}
$("#MergeFields").val('0');
}
Default.aspx.cs
我们来看看默认页面的代码。我们在这里没有做太多工作。我们只是提供一个占位符来将富文本编辑器的内容保存到您选择的位置。我没有实现保存到数据库或其他位置。您可以根据需要添加代码。
protected void Page_Load(object sender, EventArgs e)
{
string richText = Request["editor1"];
if (!string.IsNullOrEmpty(richText))
{
string a = richText;
///TODO - save this HTML values somewhere
}
}
downloader.ashx
正如您所注意到的,我们还有一个处理程序文件“downloader.ashx”。有关如何使用处理程序的更多信息,请点击此处。我们使用此文件来处理Ajax回发事件。在ProcessRequest
方法中,我们使用GUID创建一个新的文件名。这将确保我们的文件名是唯一的。然后我们调用执行繁重工作的SaveAsWord
方法。
public void ProcessRequest (HttpContext context)
{
try
{
string fileName = Guid.NewGuid().ToString() + ".docx";
string path = context.Server.MapPath("~/docs/") + fileName;
if (!(string.IsNullOrEmpty(context.Request["what"]))
&& (context.Request["what"].ToLower() == "saveasword")
&& !(string.IsNullOrEmpty(context.Request.Form[0])))
{
SaveAsWord(context.Request.Form[0], path, fileName);
}
}
catch (Exception ex)
{
context.Response.ContentType = "text/plain";
context.Response.Write(ex.ToString());
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
}
SaveAsWord
是一个包装函数,它首先为我们创建一个Word文档。然后,它调用一个函数来将邮件合并字段替换为实际值。最后,它调用一个函数来生成最终的Word文档。
private void SaveAsWord(string input, string fullFilePath, string fileNameOnly)
{
CreateDocument(fullFilePath);
input = ReplaceMailMerge(input);
generateWordDocument(input, fullFilePath, fileNameOnly);
}
使用OpenXML SDK创建Word文档非常简单。我从MSDN示例中获取了这段代码,并对其进行了调整以适应我们的需求。
private void CreateDocument(string path)
{
// Create a Wordprocessing document.
using (WordprocessingDocument myDoc =
WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
{
// Add a new main document part.
MainDocumentPart mainPart = myDoc.AddMainDocumentPart();
//Create DOM tree for simple document.
mainPart.Document = new Document();
Body body = new Body();
Paragraph p = new Paragraph();
Run r = new Run();
Text t = new Text("");
//Append elements appropriately.
r.Append(t);
p.Append(r);
body.Append(p);
mainPart.Document.Append(body);
// Save changes to the main document part.
mainPart.Document.Save();
}
}
ReplaceMailMerge
函数只是简单地查找并替换邮件合并字段,替换为我们认为合适的值。目前,我已将这些值硬编码。但是,您可以将其连接起来,使这些值来自某个数据源。
private string ReplaceMailMerge(string input)
{
input = input.Replace("{^FirstName^}", "Billy");
input = input.Replace("{^LastName^}", "Bob");
input = input.Replace("{^Title^}", "Dr.");
return input;
}
generateWordDocument
是我们的主力。它有两个部分,第一部分实际填充我们在第一步中创建的文档内容。然后它向文档添加一些骨架主体。我们依赖于AddAlternativeFormatImportPart
方法简单地将我们的富文本作为XHTML添加到文档中,这样我们就不需要进行所有的解析工作。我们将让SDK处理解析和创建文档。第二部分将新文档的路径发送回AJAX调用者。
public void generateWordDocument
(string htmlMarkup, string fullFilePath, string fileNameOnly)
{
try
{
/*----------- Generate the Document -----------------------*/
//put some title
string pageTitle = Guid.NewGuid().ToString();
//open the document
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Open(fullFilePath, true))
{
//get the document
MainDocumentPart mainPart = wordDoc.MainDocumentPart;
int altChunkIdCounter = 1;
int blockLevelCounter = 1;
string mainhtml = "<html><head><style type='text/css'>
.catalogGeneralTable{border-collapse:
collapse;text-align: left;} .catalogGeneralTable
td, th{ padding: 5px; border: 1px solid #999999; }
</style></head>
<body style='font-family:Trebuchet MS;font-size:.9em;'>"
+ htmlMarkup
+ "</body></html>";
string altChunkId = String.Format("AltChunkId{0}",
altChunkIdCounter++);
//Import data as html content using Altchunk
AlternativeFormatImportPart chunk =
mainPart.AddAlternativeFormatImportPart
(AlternativeFormatImportPartType.Html, altChunkId);
//add the chunk to the doc
using (Stream chunkStream = chunk.GetStream
(FileMode.Create, FileAccess.Write))
{
//Encoding.UTF8 is important to remove
//special characters
using (StreamWriter stringWriter
= new StreamWriter(chunkStream, Encoding.UTF8))
{
stringWriter.Write(mainhtml);
}
}
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
//insert the text in the doc
mainPart.Document.Body.InsertAt(altChunk, blockLevelCounter++);
//save the document
mainPart.Document.Save();
}
/*----------- End Generate the Document -----------------------*/
/* ------- Send the response -----------*/
//clear the response object
HttpContext.Current.Response.ClearContent();
//add the demilited string to the response object and write it.
string url = HttpContext.Current.Request.ApplicationPath
+ "/docs/" + fileNameOnly;
HttpContext.Current.Response.Write(url);
HttpContext.Current.Response.End();
/* -------End Send the response -----------*/
}
catch (Exception ex)
{
HttpContext.Current.Response.Write(ex.Message.ToString());
}
}
其他感兴趣的文件
我修改了config.js文件(位于root-->scripts-->ckeditor下),为FCKeditor创建了我自己的自定义工具栏。如果您打算使用开箱即用的编辑器,则不需要此文件。
CKEDITOR.editorConfig = function(config) {
config.toolbar = 'myToolBar';
config.toolbar_myToolBar =
[
['Source', '-', 'Save', 'NewPage', 'Preview', '-', 'Templates'],
['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-',
'Print', 'SpellChecker', 'Scayt'],
['Undo', 'Redo', '-', 'Find', 'Replace', '-',
'SelectAll', 'RemoveFormat'],
'/',
['Bold', 'Italic', 'Underline', 'Strike', '-',
'Subscript', 'Superscript'],
['NumberedList', 'BulletedList', '-', 'Outdent',
'Indent', 'Blockquote', 'CreateDiv'],
['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
['Link', 'Unlink', 'Anchor'],
['Image', 'Table', 'HorizontalRule', 'SpecialChar', 'PageBreak'],
'/',
['Styles', 'Format', 'Font', 'FontSize'],
['TextColor', 'BGColor'],
['Maximize', 'ShowBlocks']
];
};
关注点
如您所见,我将其保持在最低限度。以下是一些我们可以改进的地方:
- 目前,我不知道JQuery如何在AJAX调用中发送二进制数据。因此,我们正在发送文档路径。您会注意到我还有一个名为
DownloadFile
的方法。如果您不想使用AJAX,可以修改generateWordDocument
方法来调用此方法。然后它会将文档作为二进制数据写入响应对象。 - 我已将邮件合并下拉菜单添加到编辑器外部。如果能将其包含在工具栏中会更好。
- 编辑器工具栏上的“保存”按钮会执行回发。如果能改为AJAX调用会更好。
- 我不得不关闭default.aspx页面中的
ValidateRequest="false"
,以避免ASP.NET抛出异常,因为我们正在动态修改控件内容。一定有更好的处理方法。
历史
- 2010年9月7日:首次发布