客户端使用Silverlight+DXperience,可以在线编辑各种常见文本及富文本文档(doc、docx、rtf、txt、html……),Silverlight使用了异步提交和响应(通过WebClient和WebRequest),提交时,自行编排HTTP请求格式(请参见相应RFC标准);编辑的数据流上传至Servlet后,Servlet使用commons-fileupload-1.2.2解包数据,获取附加的请求参数及附件内容。
程序效果:
下面为客户端和服务器端程序:
(一)客户端Silverlight由部分组成:
1、主程序画面Layout部分MainPage.xaml:
<UserControl x:Class="RichEdit.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:dxre="http://schemas.devexpress.com/winfx/2008/xaml/richedit" xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars" xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:dxr="http://schemas.devexpress.com/winfx/2008/xaml/ribbon">
<UserControl.Resources>
<ResourceDictionary>
<dxre:RichEditUICommand x:Key="commands" />
<dxre:RichEditStringIdConverter x:Key="stringIdConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<dxb:BarManager Name="barManager1" ToolbarGlyphSize="Small">
<dxb:BarManager.Items>
<dxb:BarButtonItem Command="{Binding Path=FileNew, Mode=OneTime, Source={StaticResource commands}}" Name="biFileNew" />
<dxb:BarButtonItem Command="{Binding Path=FileSaveAs, Mode=OneTime, Source={StaticResource commands}}" Name="biFileSaveAs" />
<dxb:BarButtonItem Command="{Binding Path=FilePrint, Mode=OneTime, Source={StaticResource commands}}" Name="biFilePrint" />
<dxb:BarButtonItem Command="{Binding Path=FilePrintPreview, Mode=OneTime, Source={StaticResource commands}}" Name="biFilePrintPreview" />
<dxb:BarButtonItem Command="{Binding Path=FileBrowserPrint, Mode=OneTime, Source={StaticResource commands}}" Name="biFileBrowserPrint" />
<dxb:BarButtonItem Command="{Binding Path=FileBrowserPrintPreview, Mode=OneTime, Source={StaticResource commands}}" Name="biFileBrowserPrintPreview" />
<dxb:BarButtonItem Command="{Binding Path=EditUndo, Mode=OneTime, Source={StaticResource commands}}" Name="biEditUndo" />
<dxb:BarButtonItem Command="{Binding Path=EditRedo, Mode=OneTime, Source={StaticResource commands}}" Name="biEditRedo" />
<dxb:BarButtonItem Content="LoadFromWeb" Name="barButtonItemLoad" Glyph="/RichEdit;component/Images/Chrysanthemum.jpg" ItemClick="barButtonItemLoad_ItemClick" GlyphAlignment="Top" GlyphSize="Default" />
<dxb:BarButtonItem Name="barButtonItemSave" Glyph="/RichEdit;component/Images/Desert.jpg" Content="SaveToWeb" ItemClick="barButtonItemSave_ItemClick" />
<dxb:BarStaticItem Content="message..." Name="barStaticItemMsg" />
</dxb:BarManager.Items>
<dx:DockPanel>
<dxr:RibbonControl dx:DockPanel.Dock="Top" Name="ribbonControl1">
<dxr:RibbonControl.ApplicationMenu>
<dxr:ApplicationMenuInfo />
</dxr:RibbonControl.ApplicationMenu>
<dxr:RibbonDefaultPageCategory>
<dxr:RibbonPage Caption="{Binding Source={StaticResource stringIdConverter}, ConverterParameter=Caption_PageFile, Converter={StaticResource stringIdConverter}, Mode=OneTime}" Name="pageFile">
<dxr:RibbonPageGroup Caption="{Binding Source={StaticResource stringIdConverter}, ConverterParameter=Caption_GroupCommon, Converter={StaticResource stringIdConverter}, Mode=OneTime}" Name="grpCommon" ShowCaptionButton="False">
<dxb:BarButtonItemLink BarItemName="biFileNew" />
<dxb:BarButtonItemLink BarItemName="biFileSaveAs" />
<dxb:BarButtonItemLink BarItemName="biFilePrint" />
<dxb:BarButtonItemLink BarItemName="biFilePrintPreview" />
<dxb:BarButtonItemLink BarItemName="biFileBrowserPrint" />
<dxb:BarButtonItemLink BarItemName="biFileBrowserPrintPreview" />
<dxb:BarButtonItemLink BarItemName="biEditUndo" />
<dxb:BarButtonItemLink BarItemName="biEditRedo" />
</dxr:RibbonPageGroup>
<dxr:RibbonPageGroup Caption="Ribbon Page Group 1" Name="ribbonPageGroup1">
<dxb:BarButtonItemLink BarItemName="barButtonItemLoad" />
<dxb:BarButtonItemLink BarItemName="barButtonItemSave" />
</dxr:RibbonPageGroup>
<dxr:RibbonPageGroup Caption="Ribbon Page Group 2" Name="ribbonPageGroup2">
<dxb:BarStaticItemLink BarItemName="barStaticItemMsg" />
</dxr:RibbonPageGroup>
</dxr:RibbonPage>
</dxr:RibbonDefaultPageCategory>
</dxr:RibbonControl>
<dxre:RichEditControl Name="richEditControl1" BarManager="{Binding ElementName=barManager1, Mode=OneTime}" Ribbon="{Binding ElementName=ribbonControl1, Mode=OneTime}" />
</dx:DockPanel>
</dxb:BarManager>
</Grid>
</UserControl>
2、主程序逻辑部分MainPage.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using DevExpress.XtraRichEdit;
using System.Threading;
namespace RichEdit
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
String uri = "http://localhost:8888/upload/UploadServlet";
private void barButtonItemLoad_ItemClick(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e)
{
MessageBox.Show("Load Begin");
ServerStreamLoad ssl = new ServerStreamLoad();
ssl.Rp = new ShowLoadResultDelegate(ShowLoadResult);
ssl.Load(uri);
}
private void barButtonItemSave_ItemClick(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e)
{
MessageBox.Show("Save Begin");
WebRequest request = WebRequest.Create(new Uri(uri));
Dictionary<String, String> nvc = new Dictionary<String, String>();
nvc.Add("name", "value");
nvc.Add("name1", "value1");
ServerStreamSave sss = new ServerStreamSave();
sss.Rp = new ShowSaveResultDelegate(ShowSaveResult);
Stream stream = new MemoryStream();
this.richEditControl1.SaveDocument(stream, DocumentFormat.Rtf);
sss.Save(request, nvc, stream);
}
// 处理Load结果
public void ShowLoadResult(Stream stream)
{
String msg = "OK";
if (this.barStaticItemMsg.CheckAccess())
{
this.barStaticItemMsg.Content = msg;
}
else
{
Dispatcher.BeginInvoke(() =>
{
this.barStaticItemMsg.Content = msg;
});
}
using (stream)
{
if (this.richEditControl1.CheckAccess())
{
this.richEditControl1.LoadDocument(stream, DocumentFormat.Rtf);
}
else
{
Dispatcher.BeginInvoke(() =>
{
this.richEditControl1.LoadDocument(stream, DocumentFormat.Rtf);
});
}
}
}
// 处理Save结果
public void ShowSaveResult(String msg)
{
if (this.barStaticItemMsg.CheckAccess())
{
this.barStaticItemMsg.Content = msg;
}
else
{
Dispatcher.BeginInvoke(() =>
{
this.barStaticItemMsg.Content = msg;
});
}
}
}
// 处理结果的委托
public delegate void ShowLoadResultDelegate(Stream stream);
public delegate void ShowSaveResultDelegate(String s);
}
3、数据流异步下载部分ServerStreamLoad.cs:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
namespace RichEdit
{
public class ServerStreamLoad
{
ShowLoadResultDelegate rp;
public ShowLoadResultDelegate Rp
{
get { return rp; }
set { rp = value; }
}
public void Load(String uri)
{
WebClient webClient = new WebClient();
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(LoadCompletedCallBack);
webClient.OpenReadAsync(new Uri(uri));
}
private void LoadCompletedCallBack(object sender, OpenReadCompletedEventArgs e)
{
using (Stream stream = e.Result)
{
// read stream here
rp(stream);
}
}
}
}
4、数据流异步提交部分:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Collections.Generic;
using System.Windows.Threading;
namespace RichEdit
{
public class ServerStreamSave
{
static ShowSaveResultDelegate rp;
public ShowSaveResultDelegate Rp
{
get { return rp; }
set { rp = value; }
}
public void Save(WebRequest request, Dictionary<String, String> nvc, Stream stream)
{
request.Method = "POST";
UploadState us = new UploadState();
us.Req = request;
us.Nvc = nvc;
us.Stream = stream;
request.BeginGetRequestStream(new AsyncCallback(SaveRequestReadyCallBack), us);
}
private void SaveRequestReadyCallBack(IAsyncResult asyncResult)
{
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
byte[] boundarybytes = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
WebRequest request = ((UploadState)(asyncResult.AsyncState)).Req as WebRequest;
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.UseDefaultCredentials = true;
Stream rs = request.EndGetRequestStream(asyncResult);
// 1.write boundery and key-value pairs
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
IDictionary<String, String> nvc = ((UploadState)(asyncResult.AsyncState)).Nvc as IDictionary<String, String>;
foreach (string key in nvc.Keys)
{
rs.Write(boundarybytes, 0, boundarybytes.Length);
string formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
rs.Write(formitembytes, 0, formitembytes.Length);
}
rs.Write(boundarybytes, 0, boundarybytes.Length);
// 2.write head
string file = "file";
string paramName = "file";
string contentType = "unknown";
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
string header = string.Format(headerTemplate, paramName, file, contentType);
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
rs.Write(headerbytes, 0, headerbytes.Length);
// 3.write stream here,the stream will be closed by invoker
using (Stream stream = ((UploadState)(asyncResult.AsyncState)).Stream as Stream)
{
stream.Position = 0;
//byte[] buffer = System.Text.Encoding.UTF8.GetBytes("aaa我11");
byte[] data = new byte[4096];
int count = stream.Read(data, 0, 4096);
while (count > 0)
{
rs.Write(data, 0, count);
count = stream.Read(data, 0, 4096);
}
}
//rs.Write(buffer, 0, buffer.Length);
// 4.write trailer
byte[] trailer = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
rs.Write(trailer, 0, trailer.Length);
rs.Flush();
rs.Close();
request.BeginGetResponse(new AsyncCallback(SaveResponseReadyCallBack), request);
}
private void SaveResponseReadyCallBack(IAsyncResult asyncResult)
{
WebRequest request = asyncResult.AsyncState as WebRequest;
WebResponse response = request.EndGetResponse(asyncResult);
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream);
// check response result by return msg.
String msg = reader.ReadToEnd();
rp(msg);
}
}
}
}
5、异步请求提交,回调封装参数部分UploadState.cs:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace RichEdit
{
public class UploadState
{
IDictionary<String, String> nvc = null;
public IDictionary<String, String> Nvc
{
get { return nvc; }
set { nvc = value; }
}
WebRequest req = null;
public WebRequest Req
{
get { return req; }
set { req = value; }
}
Stream stream;
public Stream Stream
{
get { return stream; }
set { stream = value; }
}
}
}
(二)服务器端使用commons-fileupload-1.2.2.jar实现上传:
1、web.xml配置:
<servlet>
<display-name>UploadServlet</display-name>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.liyj.upload.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>
2、UploadServlet.java(取打包的参数和附件)
package com.liyj.upload.servlet;
import java.io.*;
import java.util.List;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletOutputStream out = null;
System.out.println("into doGet..");
try {
out = response.getOutputStream();
FileInputStream fis = new FileInputStream("D:\\file");
byte[] data = new byte[4096];
int count = fis.read(data);
while(count > 0) {
out.write(data, 0, count);
count = fis.read(data);
}
fis.close();
out.flush();
out.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) {
ServletOutputStream out = null;
System.out.println("into doPost..");
DiskFileItemFactory factory = new DiskFileItemFactory();
String path = "D:\\";
factory.setRepository(new File(path));
factory.setSizeThreshold(1024 * 1024);
ServletFileUpload upload = new ServletFileUpload(factory);
try {
out = response.getOutputStream();
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
String name = item.getFieldName();
String value = item.getString("gbk");
out.println(name + ":" + value);
} else {
String name = item.getFieldName();
String value = item.getName();
int start = value.lastIndexOf("\\");
String fileName = value.substring(start + 1);
//request.setAttribute(name, fileName);
OutputStream os = new FileOutputStream(new File(path, fileName));
InputStream is = item.getInputStream();
byte[] buffer = new byte[1024];
int length = 0;
int fileLen = 0;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
fileLen += length;
}
out.println("OK");
os.close();
is.close();
}
}
} catch (Exception e) {
try {
out.println("NG");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}
}
}
---------- THE END-----------