一、前因
因为项目中需要在silverlight 中导出Excel文件,所以就在琢磨这个事情了,开始时候的想法是能不能通过js 导出excel,在网上查了资料,试了所有找到的方法,可惜没有一个成功的。想不出其他办法,只能绕着走了,可能有点蠢,呵呵。不知道大家是怎么解决的,我把自己的发出来,抛砖引玉,不恰当的地方欢迎指出。
二、具体实现
整个解决方案包括以下几个项目,如图:
其中的BLL,Enity项目只是提供一个示例,没多大用处。ExcelExport项目中的SearchParam.cs文件中包含查询参数类,以及导航处理类。导航处理类主要是根据传入的参数集合、导出类型生成查询字符串,并传给Export.ashx处理。代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/// 查询参数类
/// </summary>
public class SearchParam
{
public string Value { get ; set ; }
public object Name { get ; set ; }
public SearchParam( string value, string name)
{
Value = value;
Name = name;
}
public override string ToString()
{
return Value + " _ " + Name;
}
}
/// <summary>
/// 导出处理
/// </summary>
public class ExportHandle
{
/// <summary>
/// 获取查询字符串
/// </summary>
/// <param name="searchParams"> 参数集合 </param>
/// <returns></returns>
private static string GetQueryString(IEnumerable < SearchParam > searchParams)
{
StringBuilder builder = new StringBuilder( " condition= " );
foreach (var param in searchParams)
{
builder.Append(param.ToString() + " " );
}
return builder.ToString().TrimEnd();
}
/// <summary>
/// 导出处理
/// </summary>
/// <param name="paramCollection"> 参数集合 </param>
/// <param name="action"> 导出类型 </param>
public static void Action(SearchParam[] paramCollection, string action)
{
string paramString = GetQueryString(paramCollection);
var uriInfo = HtmlPage.Document.DocumentUri;
var path = uriInfo.AbsoluteUri.Replace(uriInfo.AbsolutePath, "" );
string uriString = string .Concat(path, " /Export.ashx? " , " action= " , action, " & " , paramString);
HtmlPage.Window.Navigate( new System.Uri(uriString, UriKind.Absolute));
}
}
在项目ExcelExport.Web中增加了一个导出处理,用来将传入的参数进行处理,并发送到WCF服务获取导出信息,然后以excel格式返回。其中存在一个正则表达式处理类,用来对查询字符串处理返回查询参数集合。代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/// $codebehindclassname$ 的摘要说明
/// </summary>
[WebService(Namespace = " http://tempuri.org/ " )]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Export : IHttpHandler
{
#region IHttpHandler接口
public void ProcessRequest(HttpContext context)
{
var queryString = context.Request.QueryString;
string action = queryString[ " action " ];
string condition = queryString[ " condition " ];
var searchParams = GetParams(condition);
ExportClient client = new ExportClient( " WSHttpBinding_IExport " );
string content = client.GetExportString(searchParams, action);
ExportToExcel(context.Response, content,action);
}
public bool IsReusable
{
get
{
return true ;
}
}
#endregion
#region
private SearchParam[] GetParams( string condition)
{
string pattern = @" \b(\S+)_(\S+)\b " ;
IRegular regular = new Regular();
RegularInfo info = new RegularInfo(condition, pattern, RegexOptions.IgnoreCase);
var matchs = regular.Filter(info);
SearchParam[] searchParams = new SearchParam[matchs.Length];
int i = 0 ; SearchParam param;
foreach (Match match in matchs)
{
param = new SearchParam();
param.Name = match.Groups[ 1 ].Value;
param.Value = match.Groups[ 2 ].Value;
searchParams[i] = param;
i ++ ;
}
return searchParams;
}
protected void ExportToExcel(HttpResponse response, string content, string action)
{
response.Clear();
response.Buffer = true ;
response.Charset = " GB2312 " ;
// page.Response.Charset = "UTF-8";
response.AppendHeader( " Content-Disposition " , " attachment;filename= " + action + " .xls " );
response.ContentEncoding = System.Text.Encoding.GetEncoding( " GB2312 " ); // 设置输出流为简体中文
response.ContentType = " application/ms-excel " ; // 设置输出文件类型为excel文件。
response.Write(content);
response.End();
}
#endregion
}
正则表达式处理代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
Match[] Filter(RegularInfo info);
}
/// <summary>
/// 正则表达式处理
/// </summary>
public class Regular:IRegular
{
#region IRegular 成员
public Match[] Filter(RegularInfo info)
{
var matchCollection = Regex.Matches(info.Source, info.Pattern, info.Options);
Match[] result = new Match[matchCollection.Count];
int i = 0 ;
foreach (Match match in matchCollection)
{
result[i] = match;
i ++ ;
}
return result;
}
#endregion
}
/// <summary>
/// 正则表达式信息
/// </summary>
public class RegularInfo
{
public string Source { get ; set ; }
public string Pattern { get ; set ; }
public RegexOptions Options { get ; set ; }
public RegularInfo( string source, string pattern, RegexOptions options)
{
Source = source;
Pattern = pattern;
Options = options;
}
}
项目ExportConfig中包括的内容是最重要的,主要分为接口、默认实现、导出处理、查询实体类四部分。接口分别为(1)IExportContent存在两个方法,Print()用来返回一行中各列的值,GetFormat() 返回单元格的样式,这里的样式必须为css样式;(2)IContentIterator内容迭代接口;(3)IExportFactory存在CreateHead,CreateContent两个方法,分别用来返回表头,表内容迭代。默认实现为:ExportContent,ContentAggregate,ContentIterator,ExportAbstractFactory。导出处理放在ExportHandle类。
接口代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/// 内容迭代接口
/// </summary>
public interface IContentIterator
{
bool HasNext();
IExportContent Next();
}
/// <summary>
/// 内容接口
/// </summary>
public interface IExportContent
{
string [] Print();
string GetFormat();
}
/// <summary>
/// 表信息工厂接口
/// </summary>
public interface IExportFactory
{
IExportContent CreateHead();
IContentIterator CreateContent(SearchCollection condition);
}
导出处理类:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
private IExportFactory factory;
public ExportHandle(IExportFactory factory)
{
this .factory = factory;
}
public string GetExportString(SearchCollection condition)
{
var head = factory.CreateHead();
StringBuilder build = new StringBuilder( " <table> " );
AppandString(head, build);
var iterator = factory.CreateContent(condition);
IExportContent item;
while (iterator.HasNext())
{
item = iterator.Next();
AppandString(item, build);
}
build.Append( " </table> " );
return build.ToString();
}
private void AppandString(IExportContent item,StringBuilder build)
{
string format = item.GetFormat(), value;
bool hasFormat = ! string .IsNullOrEmpty(format);
build.Append( " <tr> " );
var stringList = item.Print();
foreach (var str in stringList)
{
if (hasFormat)
{
value = string .Format( " <td style=\ " border - color:Gray; border - width:thin;border - style:ridge;{ 0 }\ " >{1}</td> " , format, str);
}
else
{
value = string .Format( " <td style=\ " border - color:Gray; border - width:thin;border - style:ridge\ " >{0}</td> " , str);
}
build.Append(value);
}
build.Append( " </tr> " );
}
}
最后一个项目ExportService 是用来提供WCF服务的,这里还存在一个设置,可以在web.config中添加导出的类名,以及程序集名称 ,如图:
获取配置信息后,通过反射创建实例。然后将查询参数、实例传给导出处理类进行操作。如下代码所示:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
[ServiceBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Export : IExport
{
#region IExport 成员
public string GetExportString(SearchParam[] paramCollection, string action)
{
string [] infos = ConfigurationSettings.AppSettings[action].Split( ' | ' );
SearchCollection condition = null ;
if (paramCollection != null )
{
condition = new SearchCollection();
foreach (var param in paramCollection)
{
condition.Add(param);
}
}
IExportFactory instance = (IExportFactory)CommonHelp.CreateInstance(infos[ 1 ], infos[ 0 ], null );
ExportHandle handle = new ExportHandle(instance);
return handle.GetExportString(condition);
}
#endregion
}
public class CommonHelp
{
public static object CreateInstance( string type, string assembly, object [] args)
{
return Assembly.Load(assembly).CreateInstance(type, true , BindingFlags.Default, null , args, null , null );
}
}
另外,WCF调用可能存在跨域问题,在IIS网站根目录下放这个clientaccesspolicy.xml文件就OK了。第一次写可能有点乱,觉得还点用,就将就着看下吧,呵呵。完整的Demo下载:
/Files/WGZ_Home/ExcelExport.rar
——————————————————————————————————————————————————————————————————————————————————
经(笨笨的猪)朋友的提醒,现在把handle去掉了,并进行了一些小的修改,silverlight直接引用WCF服务,并返回byte[],然后通过SaveDialog保存。修改后的关于silverlight的
处理代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
ObservableCollection < SearchParam > paramCollection = new ObservableCollection < SearchParam > ();
var param = new SearchParam();
param.Name = " 2 " ;
param.Value = " 2 " ;
paramCollection.Add(param);
SaveFileDialog dialog = new SaveFileDialog();
ExportHandle( " ATPExport " , paramCollection, dialog);
}
public static void ExportHandle( string action,ObservableCollection < ExeclExport.Export.SearchParam > paramCollection,SaveFileDialog dialog)
{
try
{
dialog.DefaultExt = " .txt " ;
dialog.Filter = " Excel Files|*.xls|Log Files|*.doc " ;
dialog.FilterIndex = 1 ;
}
catch ( Exception ex )
{
HtmlPage.Window.Alert(ex.Message);
return ;
}
bool ? dialogResult = dialog.ShowDialog();
if ( dialogResult == true )
{
Export.ExportClient client = new ExeclExport.Export.ExportClient();
client.GetExportStringCompleted += delegate ( object sender, ExeclExport.Export.GetExportStringCompletedEventArgs e)
{
if (e.Error == null )
{
using ( Stream fs = dialog.OpenFile() )
{
var resultBytes = e.Result;
fs.Write(resultBytes, 0 , resultBytes.Length);
fs.Close();
HtmlPage.Window.Alert( " 保存成功 " );
}
return ;
}
client.CloseAsync();
HtmlPage.Window.Alert(e.Error.Message);
};
client.GetExportStringAsync(paramCollection, action);
}
}
完整Demo如下:/Files/WGZ_Home/ExcelExport.rar