注:本文的HTM页面均位于Discuz.Web项目中,大家可以到官方下面最终的程序。
         在去年我曾写过一篇文章:“推荐一个Silverlight多文件(大文件)上传的开源项目”。之后有不少朋友询问这个项目示例在开发和配置上的一些问题。当时因为时间有限没有做过多的说明,导致有些问题在大家下载完源码之后运行时才出现。今天就以这个项目为原型,简要介绍一下在DiscuzNT上是如果在该项目基本上,通过完善权限管理,文件大小控制,添加缩略图效果等功能来大体阐述一下如果开发一个真正的silverlight应用,而不是一个简单的DEMO.
      当然本文中所列出的源码是通过reflector获取并添加相应注释的。最终的源码还是要以开放出来的为准,呵呵:)

      好了,开始今天的正文吧!
   
      首先,看一下这个插件在DiscuzNT中的实际运行效果:    
    
    
    
    
    

    
 
     
     
       当我们在网页中点击“批量上传”按钮时,会运行如下JS脚本(文件位于Discuz.Web\templates\default_postattachments.htm):
function LoadSilverlight(pluginID, max) {
     
     Silverlight.createObject(
         
"silverlight/UploadFile/ClientBin/MultiFileUpload.xap"
         $(
"silverlightControlHost"),         
         pluginID,                         
         {     
             width: 
'500',
             height: 
'440',
             inplaceInstallPrompt: 
'true',
             isWindowless: 
'true',
             background: 
'transparent',
             version: 
'2.0',
             autoUpgrade: 
'true'
         },
         {
             onLoad: onLoad, 
             onError: onSilverlightError
         },
         
<%csharp%>
         string authToken 
= Discuz.Common.DES.Encode(oluserinfo.Olid.ToString() + "," + 
         oluserinfo.Username.ToString(), oluserinfo.Password.Substring(
010)).Replace("+""[");
         
<%/csharp%> 
         "forumid={forumid},authToken={authToken},max=" + max,
         
"");
}
 
        其会将当前版块id(forumid),认证Token,最大上传数等信息以参数形式传给SL插件,而我专门定义了一个方法用于获取相应的参数并绑定到sl变量,如下(Page.xaml.cs): 
/// <summary>
 
/// 加载配置参数
 
/// </summary>
 
/// <param name="initParams"></param>
 private void LoadConfiguration(IDictionary<stringstring> initParams)
 {
     
string tryTest = string.Empty;

     
//加载定制配置信息串
     _customParams = initParams["forumid"];

     
if (initParams.ContainsKey("MaxUploads"&& !string.IsNullOrEmpty(initParams["MaxUploads"]))
         
int.TryParse(initParams["MaxUploads"], out _maxUpload);            

     
if (initParams.ContainsKey("MaxFileSizeKB"&& !string.IsNullOrEmpty(initParams["MaxFileSizeKB"]))
     {
         
if (int.TryParse(initParams["MaxFileSizeKB"], out _maxFileSize))
             _maxFileSize 
= _maxFileSize * 1024;
     }

     
if (initParams.ContainsKey("FileFilter"&& !string.IsNullOrEmpty(initParams["FileFilter"]))
         _fileFilter 
= initParams["FileFilter"];          

     
if (initParams.ContainsKey("forumid"&& !string.IsNullOrEmpty(initParams["forumid"]))
         _forumid 
= Utils.StrToInt(initParams["forumid"], 0);

     
if (initParams.ContainsKey("max"&& !string.IsNullOrEmpty(initParams["max"]))
         _maxAttachments 
= Utils.StrToInt(initParams["max"], 0);

     CredentialInfo _creInfo
= Utils.GetCredentialInfo();
     
if (_creInfo.UserID <= 0)
     {
         ShowMessageBox(
"您未登陆系统");
         SetUploadButton(
false);
         
return;
     }
     
else
     {
         MixObjectsSoapClient _client 
= Utils.CreateServiceClient();
         _client.GetAttachmentUploadSetCompleted 
+= new EventHandler<GetAttachmentUploadSetCompletedEventArgs>
                                (_client_GetAttachmentUploadSetCompleted);
         _client.GetAttachmentUploadSetAsync(_creInfo, _forumid);
     }
 }

        
        
        大家看到在该方法在获取相应初始化参数(initParams)后,会调用Utils.GetCredentialInfo()来获取 用户登陆信息:
/// <summary>
/// 获取认证信息
/// </summary>
/// <returns></returns>
public static CredentialInfo GetCredentialInfo()
{
        CredentialInfo _creinfo 
= new CredentialInfo();
        _creinfo.UserID 
= Utils.StrToInt(Utils.GetCookie("userid"), 0);
        _creinfo.Password 
= Utils.GetCookie("password");

        
if (App.GetInitParmas.ContainsKey("authToken"&& !string.IsNullOrEmpty(App.GetInitParmas["authToken"]))
            _creinfo.AuthToken 
= App.GetInitParmas["authToken"];

        
if (App.GetInitParmas.ContainsKey("forumid"&& !string.IsNullOrEmpty(App.GetInitParmas["forumid"]))
            _creinfo.ForumID 
= StrToInt(App.GetInitParmas["forumid"], 0);

        
return _creinfo;
}

        
    其中最主要的就是获取相应的UserID,而这个操作是交给GetCookie来完成的:
      

public static string GetCookie(String key)
{
     
if (string.IsNullOrEmpty(HtmlPage.Document.Cookies))
         
return null;

     
//找到想应的cookie键值
     string result = (from c in
                          (from cookie 
in HtmlPage.Document.Cookies.Split(';')
                           
where cookie.Contains(key + "=")
                           select cookie.Split(
'&')).FirstOrDefault()
                      
where c.Contains(key + "=")
                      select c).FirstOrDefault().ToString();

     
if(string.IsNullOrEmpty(result))
         
return null;

     
return result.Substring(result.IndexOf(key + "="+ key.Length + 1);
}

 
      其主要是通过用户本地的Cookie,来获取相应的用户信息。
   
      如果用户的Cookie有效(已登陆过),则直接获取该用户所在用户组及其它相关联的权限信息, 如果无效,则提示用户登陆,同时将SL中的几个上传按钮“置灰”,以免未登陆的用户上传附件。
  
      下面就是其向服务器请求谁信息的代码:
 MixObjectsSoapClient _client = Utils.CreateServiceClient();
 _client.GetAttachmentUploadSetCompleted 
+= new EventHandler<GetAttachmentUploadSetCompletedEventArgs>
                               (_client_GetAttachmentUploadSetCompleted);
 _client.GetAttachmentUploadSetAsync(_creInfo, _forumid);
     
      上面客户端请求下面的服务端代码:     
/// <summary>
/// 通过指定用户认证信息来获得该用户的上传设置信息
/// </summary>
/// <param name="creinfo">用户认证信息</param>
/// <returns></returns>
[WebMethod]
public UploadSetInfo GetAttachmentUploadSet(CredentialInfo creinfo)
{
    
if (AuthenticateUser(creinfo))
    {
        UserInfo userinfo 
= Discuz.Forum.Users.GetUserInfo(creinfo.UserID);
        
if (userinfo == null)
            
return new UploadSetInfo(""""00false0"当前用户信息无效,请尝试刷新");

        UserGroupInfo usergroupinfo 
= Discuz.Forum.UserGroups.GetUserGroupInfo(userinfo.Groupid);
        
if (usergroupinfo == null)
            
return new UploadSetInfo(""""00false0"当前用户所属用户组信息无效");


        ForumInfo forum 
= Discuz.Forum.Forums.GetForumInfo(creinfo.ForumID);
        
if (forum == null)
            
return new UploadSetInfo(nullnull00false0"当前版块信息无效,请尝试刷新");

        
//得到用户可以上传的文件类型
        StringBuilder sbAttachmentTypeSelect = new StringBuilder();
        
if (!usergroupinfo.Attachextensions.Trim().Equals(""))
        {
            sbAttachmentTypeSelect.Append(
"[id] in (");
            sbAttachmentTypeSelect.Append(usergroupinfo.Attachextensions);
            sbAttachmentTypeSelect.Append(
")");
        }

        
if (!forum.Attachextensions.Equals(""))
        {
            
if (sbAttachmentTypeSelect.Length > 0)
                sbAttachmentTypeSelect.Append(
" AND ");

            sbAttachmentTypeSelect.Append(
"[id] in (");
            sbAttachmentTypeSelect.Append(forum.Attachextensions);
            sbAttachmentTypeSelect.Append(
")");
        }
        
string attachextensions = Discuz.Forum.Attachments.GetAttachmentTypeArray(sbAttachmentTypeSelect.ToString());
        
string attachextensionsnosize = Discuz.Forum.Attachments.GetAttachmentTypeString(sbAttachmentTypeSelect.ToString());

        
//得到今天允许用户上传的附件总大小(字节)
        int MaxTodaySize = 0;
        
if (creinfo.UserID > 0)
            MaxTodaySize 
= Discuz.Forum.Attachments.GetUploadFileSizeByuserid(creinfo.UserID);

        
int attachsize = usergroupinfo.Maxsizeperday - MaxTodaySize;//今天可上传大小

        
bool canpostattach = false//是否允许上传附件
        
//是否有上传附件的权限
        if (Discuz.Forum.Forums.AllowPostAttachByUserID(forum.Permuserlist, creinfo.UserID))
            canpostattach 
= true;
        
else
        {
            
if (forum.Postattachperm == "")
            {
                
if (usergroupinfo.Allowpostattach == 1)
                    canpostattach 
= true;
            }
            
else
            {
                
if (Discuz.Forum.Forums.AllowPostAttach(forum.Postattachperm, usergroupinfo.Groupid))
                    canpostattach 
= true;
            }
        }
        
return new UploadSetInfo(attachextensions, attachextensionsnosize, MaxTodaySize, attachsize, 
                            canpostattach , usergroupinfo.Maxattachsize, 
"");
    }

    
return new UploadSetInfo(""""00false0"当前用户信息无效,请尝试刷新");
}

 
     该方法的首先会访问AuthenticateUser方法来进行用户身份验证:
   
/// <summary>
/// WEB权限认证
/// </summary>
/// <param name="creinfo">认证信息</param>
/// <returns>是否通过验正</returns>
private bool AuthenticateUser(CredentialInfo creinfo)
{
    
if (creinfo.ForumID > 0)
    {
        
int olid = Discuz.Forum.OnlineUsers.GetOlidByUid(creinfo.UserID);
        
if (olid > 0)
        {
            OnlineUserInfo oluserinfo 
= Discuz.Forum.OnlineUsers.GetOnlineUser(olid);
            
if (oluserinfo.Userid == creinfo.UserID && 
                Utils.UrlEncode(Discuz.Forum.ForumUtils.SetCookiePassword(oluserinfo.Password, 
                 GeneralConfigs.GetConfig().Passwordkey)) 
== creinfo.Password &&//检测用户id和口令
                creinfo.AuthToken == DES.Encode(string.Format("{0},{1}", oluserinfo.Olid.ToString(), 
                  oluserinfo.Username.ToString()), oluserinfo.Password.Substring(
010)).Replace("+""["))//检查认证信息
            {
                
return true;
            }
        }
    }
    
return false;
}
    
       其会对用户的UserId与用户在线表中的数据进行比对,以确保其信息有效,同时还会检查AuthToken来避免用户通过伪造用户信息来进行信息提交。当上面方法返回TRUE时,则将对用户所在版块的权限信息进行获取,并返回一个名为UploadSetInfo类实例,其包括:
    1.用户可以上传的文件类型
    2.用户可以上传的文件类型(不带上传数据大小)
    3.得到今天允许用户上传的附件总大小(字节)
    4.是否允许上传附件
    5.单个附件大小
    6.最大允许上传的附件数
    7.错误信息
     
#region 上传设置信息类
    
/// <summary>
    
/// 上传设置信息类
    
/// </summary>
    public class UploadSetInfo
    {
        
public UploadSetInfo()
        { }

        
public UploadSetInfo(string attachExtensions, string attachExtensionsNoSize, int maxTodaySize, 
                  
int attachSize, bool canPostAttach, int maxAttachSize, string errMessage)
        {
            m_attachExtensions 
= attachExtensions;
            m_attachExtensionsNoSize 
= attachExtensionsNoSize;
            m_maxTodaySize 
= maxTodaySize;
            m_attachSize 
= attachSize;
            m_canPostAttach 
= canPostAttach;
            m_maxAttachSize 
= maxAttachSize;
            m_errMessage 
= errMessage;
            m_maxAttachments 
= GeneralConfigs.GetConfig().Maxattachments;
        }

        
private string m_attachExtensions;
        
/// <summary>
        
/// 用户可以上传的文件类型
        
/// </summary>
        public string AttachExtensions
        {
            
get { return m_attachExtensions; }
            
set { m_attachExtensions = value; }
        }

        
private string m_attachExtensionsNoSize;
        
/// <summary>
        
/// 用户可以上传的文件类型(不带上传数据大小)
        
/// </summary>
        public string AttachExtensionsNoSize
        {
            
get { return m_attachExtensionsNoSize; }
            
set { m_attachExtensionsNoSize = value; }
        }

        
private int m_maxTodaySize;
        
/// <summary>
        
/// 得到今天允许用户上传的附件总大小(字节)
        
/// </summary>
        public int MaxTodaySize
        {
            
get { return m_maxTodaySize; }
            
set { m_maxTodaySize = value; }
        }

        
private int m_attachSize;
        
/// <summary>
        
/// 今天可上传的大小
        
/// </summary>
        public int AttachSize
        {
            
get { return m_attachSize; }
            
set { m_attachSize = value; }
        }

        
private bool m_canPostAttach;
        
/// <summary>
        
/// 是否允许上传附件
        
/// </summary>
        public bool CanPostAttach
        {
            
get { return m_canPostAttach; }
            
set { m_canPostAttach = value; }
        }

        
private int m_maxAttachSize;
        
/// <summary>
        
/// 单个附件大小
        
/// </summary>
        public int MaxAttachSize
        {
            
get { return m_maxAttachSize; }
            
set { m_maxAttachSize = value; }
        }

        
private string m_errMessage;
        
/// <summary>
        
/// 错误信息
        
/// </summary>
        public string ErrMessage
        {
            
get { return m_errMessage; }
            
set { m_errMessage = value; }
        }

        
private int m_maxAttachments;
        
/// <summary>
        
/// 最大允许上传的附件数
        
/// </summary>
        public int Maxattachments
        {
            
get { return m_maxAttachments; }
            
set { m_maxAttachments = value; }
        }
    }
    
#endregion


        如果一切顺利,客户端会获取相应的UploadSetInfo实例信息来进行SL插件的信息绑定,也就是之前第二张图中所说的信息内容(红框部分)。