DiscuzNT使用Silverlight进行多文件上传(下)

简介:
  如果当前用户通过验证,就可以通过SL上传附件了,因为用户上传的附件要进行实时统计,以即时更新已上传附件的总和大小,来防止用户上传过量的附件),所以我在打开文件对话框事件中加入了到已上传附件大小的统计以便进行控件:
///   <summary>
///  选择文件对话框事件
///   </summary>
///   <param name="sender"></param>
///   <param name="e"></param>
private   void  SelectFilesButton_Click( object  sender, RoutedEventArgs e)
{
    
if  (AttachmentList.Count  >=  _maxAttachments)
    {
        ShowMessageBox(
" \r\n您上传的文件数已达到系统规定的上限:  "   +  _maxAttachments  +   " . " );
        
return ;
    }

    OpenFileDialog ofd 
=   new  OpenFileDialog();
    ofd.Multiselect 
=   true ;

    
try
    {
        
if ( ! string .IsNullOrEmpty(_fileFilter))
            ofd.Filter 
=  _fileFilter;
    }
    
catch (ArgumentException ex)
    {
        ShowMessageBox(
" 错误的文件过滤配置:  "   +  ex.Message);
    }

    
if  (ofd.ShowDialog()  ==   true )
    {
        
if  (filecount  ==   0 )
            filecount 
=  AttachmentList.Count;

        
foreach  (FileInfo file  in  ofd.Files)
        {
            
if  ((filecount  +   1 >  _maxAttachments)
            {
                ShowMessageBox(
" \r\n您上传的文件数已达到系统规定的上限:  "   +  _maxAttachments  +   " . " );
                
return ;
            }
            filecount
++ ;

            
string  fileName  =  file.Name;
            UserFile userFile 
=   new  UserFile();
            userFile.FileName 
=  file.Name;
            userFile.FileStream 
=  file.OpenRead();
            userFile.ViewStream 
=  file.OpenRead();

            
// 总上传值在规定范围内时
             if  (_todayAttachSize  <  (_todayUploadSize  +  _wantUploadSize  +  userFile.FileStream.Length))
            {
                ShowMessageBox(
" \r\n当前附件大小:  "   +  Math.Round(( decimal )userFile.FileStream.Length  /   1024   /   1024 2 )
                 
+   "  MB, 而今天还可以上传: "   +  Math.Round(( decimal )(_todayAttachSize  -  _todayUploadSize)  /   1024   /   1024 2 +   "  MB. " );
                
break ;
            }
            
// 当单个文件大小大于最大上传附件尺寸时
             if  (userFile.FileStream.Length  >  _maxFileSize)
            {
                ShowMessageBox(
" \r\n当前附件大小:  "   +  Math.Round(( decimal )userFile.FileStream.Length  /   1024   /   1024 2 )
                
+   "  MB, 而单个附件允许最大尺寸为:  "   +  Math.Round(( decimal )_maxFileSize  /   1024   /   1024 2 +   "  MB.\r\n " );
                
break ;
            }

            
// 向文件列表中添加文件信息
            _files.Add(userFile);
            _wantUploadSize 
+=  userFile.FileStream.Length;  
        }
    }
}       

    
          这样就从选择附件方面杜绝了上述情况的发生。    
    
         之后,当用户选择了上传的附件,并加载到上传列表后点击上传按钮时,SL会将当前要上传的文件进行切块(4 * 4096 byte),并分块上传,代码如下:
 
///   <summary>
///  上传文件
///   </summary>
private   void  UploadAdvanced()
{
    
    
byte [] buffer  =   new   byte [ 4   *   4096 ];
    
int  bytesRead  =  _file.FileStream.Read(buffer,  0 , buffer.Length);

    
// 文件是否上传完毕?
     if  (bytesRead  !=   0 )
    {
        _dataSent 
+=  bytesRead;

        
if  (_dataSent  ==  _dataLength)
            _lastChunk 
=   true ; // 是否是最后一块数据,这样WCF会在服务端根据该信息来决定是否对临时文件重命名

        
// 上传当前数据块
        _client.StoreFileAdvancedAsync(_file.FileName, buffer, bytesRead, _initParams, _firstChunk, _lastChunk, 
                 Utils.GetCredentialInfo());

        
// 在第一条消息之后一直为false
        _firstChunk  =   false ;

        
// 通知上传进度修改
        OnProgressChanged();
    }
    
else
    {
        
// 当上传完毕后
        _file.FileStream.Dispose();
        _file.FileStream.Close();

        _client.ChannelFactory.Close();          
    }
}

       
        注意上面的StoreFileAdvancedAsync方法就是要请求的服务端代码,下面即是其服务端代码:     

 
  ///   <summary>
 
///  上传附件
 
///   </summary>
 
///   <param name="fileName"> 文件名称 </param>
 
///   <param name="data"> 上传的字节数据 </param>
 
///   <param name="dataLength"> 数据长度 </param>
 
///   <param name="parameters"> 上传参数 </param>
 
///   <param name="firstChunk"> 是否第一块数据 </param>
 
///   <param name="lastChunk"> 是否最后一块数据 </param>
 [WebMethod]
 
public  AttachmentInfo StoreFileAdvanced( string  fileName,  byte [] data,  int  dataLength,  string  parameters, 
                                        
bool  firstChunk,  bool  lastChunk, CredentialInfo creinfo)
 {
     
if  (AuthenticateUser(creinfo))
     {
         UploadSetInfo uploadSetInfo 
=  GetAttachmentUploadSet(creinfo);
         
string  fileextname  =  Utils.CutString(fileName, fileName.LastIndexOf( " . " +   1 ).ToLower();

         
if  (uploadSetInfo.CanPostAttach  &&  uploadSetInfo.AttachExtensionsNoSize.IndexOf(fileextname)  >=   0   &&  
             uploadSetInfo.AttachSize 
>  dataLength  &&  Utils.StrIsNullOrEmpty(uploadSetInfo.ErrMessage))
         {
             
string  uploadFolder  =  GetUploadFolder(fileName, creinfo.ForumID.ToString());
             
string  tempFileName  =  fileName  +  _tempExtension;

             
if  (firstChunk)
             {
                 
// 删除临时文件
                  if  (File.Exists(@HostingEnvironment.ApplicationPhysicalPath  +   " /upload/temp/ "   +  tempFileName))
                     File.Delete(@HostingEnvironment.ApplicationPhysicalPath 
+   " /upload/temp/ "   +  tempFileName);

                 
// 删除目录文件
                  if  (File.Exists(uploadFolder  +   " / "   +  fileName))
                     File.Delete(uploadFolder 
+   " / "   +  fileName);
             }


             FileStream fs 
=  File.Open(@HostingEnvironment.ApplicationPhysicalPath  +   " /upload/temp/ "   +  
                                        tempFileName, FileMode.Append);
             fs.Write(data, 
0 , dataLength);
             fs.Close();

             
if  (lastChunk)
             {
                 File.Move(HostingEnvironment.ApplicationPhysicalPath 
+   " /upload/temp/ "   +  tempFileName, 
                                       uploadFolder 
+   " / "   +  fileName);
                 
return  Discuz.Forum.Attachments.GetAttachmentInfo(AddAttachment(fileName, creinfo));
             }
         }
     }
     
return   null ;
 }

        上面代码会将客户端发送过来的分块数据进行组装到临时文件中,当该文件的所有分块数据传输完毕, 就会将该临时文件移动到指定的附件文件夹中,同时删除 相应临时数据。大家注意到了,在该方法开始部分 还调用了AuthenticateUser方法(之前说明),这主要就是对安全性的考虑,必定HTTP是 一种无状态协议呀。
 
       上面的代码中这一行判断:

if  (uploadSetInfo.CanPostAttach  &&  uploadSetInfo.AttachExtensionsNoSize.IndexOf(fileextname)  >=   0  
    
&&  uploadSetInfo.AttachSize  >  dataLength  &&
             Utils.StrIsNullOrEmpty(uploadSetInfo.ErrMessage))
             
        即是对当前用户上传信息(包括已上传附件大小,数量等)进行检验,以免出现上传数据超过系统限制的情况。
       在该方法的最后一行,会调用AddAttachment(fileName, creinfo)来进行相应附件信息的初始化绑定,因为论坛中每个主题包括回帖都可以有附件,只不过对附件的扩展名和大小会有限制,所以这里通过该方法进行封装,下面是其核心代码:

 
///   <summary>
///  添加附件
///   </summary>
///   <param name="fileName"> 文件名称 </param>
///   <param name="creinfo"> 认证信息 </param>
///   <returns> 返回当前插入的附件id </returns>
private   int  AddAttachment( string  fileName, CredentialInfo creinfo)
{
    
string  UploadDir  =  GetUploadFolder(fileName, creinfo.ForumID.ToString());
    AttachmentInfo attachmentinfo 
=   new  AttachmentInfo();
    
string  fileextname  =  Utils.CutString(fileName, fileName.LastIndexOf( " . " +   1 ).ToLower();
    
string  newfilename  =  (Environment.TickCount  &   int .MaxValue).ToString()  +   new  Random().Next( 1000 9999
                       
+   " . "   +  fileextname;

    
try
    {
        
//  如果是bmp jpg png图片类型
         if  ((fileextname  ==   " bmp "   ||  fileextname  ==   " jpg "   ||  fileextname  ==   " jpeg "   ||  fileextname  ==   " png " ))
        {
            
if  (Discuz.Common.Utils.FileExists(UploadDir  +  fileName))
            {
                System.Drawing.Image img 
=  System.Drawing.Image.FromFile(UploadDir  +  fileName);

                
if  (config.Attachimgmaxwidth  >   0   &&  img.Width  >  config.Attachimgmaxwidth)
                    attachmentinfo.Sys_noupload 
=   " 图片宽度为 "   +  img.Width.ToString()  +   " , 系统允许的最大宽度为 "  
                                   
+  config.Attachimgmaxwidth.ToString();

                
if  (config.Attachimgmaxheight  >   0   &&  img.Height  >  config.Attachimgmaxheight)
                    attachmentinfo.Sys_noupload 
=   " 图片高度为 "   +  img.Width.ToString()  +   " , 系统允许的最大高度为 "  
                                   
+  config.Attachimgmaxheight.ToString();

                
if  (config.Watermarkstatus  ==   0 )
                    attachmentinfo.Filesize 
=   new  FileInfo(UploadDir  +  fileName).Length;
                
else
                {
                    
if  (config.Watermarktype  ==   1   &&  File.Exists(Utils.GetMapPath(BaseConfigs.GetForumPath 
                                           
+   " watermark/ "   +  config.Watermarkpic)))
                        Discuz.Forum.ForumUtils.AddImageSignPic(img, UploadDir 
+  newfilename, 
                                        Utils.GetMapPath(BaseConfigs.GetForumPath 
+   " watermark/ "   +  config.Watermarkpic), 
                                        config.Watermarkstatus, config.Attachimgquality, config.Watermarktransparency);
                    
else
                    {
                        
string  watermarkText;
                        watermarkText 
=  config.Watermarktext.Replace( " {1} " , config.Forumtitle);
                        watermarkText 
=  watermarkText.Replace( " {2} " " http:// "   +  DNTRequest.GetCurrentFullHost()  +   " / " );
                        watermarkText 
=  watermarkText.Replace( " {3} " , Utils.GetDate());
                        watermarkText 
=  watermarkText.Replace( " {4} " , Utils.GetTime());

                        Discuz.Forum.ForumUtils.AddImageSignText(img, UploadDir 
+  newfilename, watermarkText,
                            config.Watermarkstatus, config.Attachimgquality, config.Watermarkfontname, config.Watermarkfontsize);
                    }
                    System.IO.File.Delete(UploadDir 
+  fileName);
                    
//  获得加水印后的文件长度
                    attachmentinfo.Filesize  =   new  FileInfo(UploadDir  +  newfilename).Length;
                }
            }
        }
        
else
        {
            System.IO.File.Move(UploadDir 
+  fileName, UploadDir  +  newfilename);
            attachmentinfo.Filesize 
=   new  FileInfo(UploadDir  +  newfilename).Length;
        }
    }
    
catch {}

    
if  (Discuz.Common.Utils.FileExists(UploadDir  +  fileName))
    {
        attachmentinfo.Filesize 
=   new  FileInfo(UploadDir  +  fileName).Length;
        attachmentinfo.Filename 
=  GetDirInfo(fileName, creinfo.ForumID.ToString())  +  fileName;
    }

    
if  (Discuz.Common.Utils.FileExists(UploadDir  +  newfilename))
    {
        attachmentinfo.Filesize 
=   new  FileInfo(UploadDir  +  newfilename).Length;
        attachmentinfo.Filename 
=  GetDirInfo(newfilename, creinfo.ForumID.ToString())  +  newfilename;
    }
    
    attachmentinfo.Uid 
=  creinfo.UserID;
    attachmentinfo.Description 
=  fileextname;
    attachmentinfo.Filetype 
=  GetContentType(fileextname);
    attachmentinfo.Attachment 
=  fileName;
    attachmentinfo.Downloads 
=   0 ;
    attachmentinfo.Postdatetime 
=  DateTime.Now.ToString();
    attachmentinfo.Sys_index 
=   0 ;

    
return  Discuz.Data.DatabaseProvider.GetInstance().CreateAttachment(attachmentinfo);
}

 
         通过上面的方法就实现了将附件与相应的主题进行绑定功能同时为相应的图片附件加上水印。   
    
         到这里,主要的代码就介绍的差不多了。
    
         当用户完成上传之后,点击“返回”按钮时,会触发下面事件:   

 
///   <summary>
///  返回按钮事件
///   </summary>
///   <param name="sender"></param>
///   <param name="e"></param>
private   void  FinishUpload_Click( object  sender, RoutedEventArgs e)
{
    GetAttachmentList();
}

[ScriptableMember]
public   void  GetAttachmentList()
{
    StringBuilder sb_attachments 
=   new  StringBuilder();
    sb_attachments.Append(
" [ " );
    
foreach  (AttachmentInfo attachmentInfo  in  AttachmentList)
    {
        sb_attachments.Append(
string .Format( " {{'aid' : {0}, 'attachment' : '{1}', 'description' : '{2}',  'filename' : 
                         ' {3} ' ' filesize '  :{ 4 },  ' filetype '  :  ' {5} ' ' Uid '  : { 6 }}}, " ,
            attachmentInfo.Aid,
            attachmentInfo.Attachment,
            attachmentInfo.Description.Trim(),
            attachmentInfo.Filename.Trim(),
            attachmentInfo.Filesize,
            attachmentInfo.Filetype,
            attachmentInfo.Uid
            ));
    }
    
if  (sb_attachments.ToString().EndsWith( " , " ))
        sb_attachments.Remove(sb_attachments.Length 
-   1 1 );

    sb_attachments.Append(
" ] " );

    
// 调用js端注册事件
    javaScriptableObject.OnUploadAttchmentList(JsonCharFilter(sb_attachments.ToString()));
}

 
        GetAttachmentList方法会调用页面的JS事件并将“已上传”的数据发给WEB页面,而相应的页面事件绑定代码如下(文件位于Discuz.Web\templates\default_postattachments.htm):
function  onLoad(plugin, userContext, sender) {
     
// 只读属性,标识 Silverlight 插件是否已经加载。
      if  (sender.getHost().IsLoaded) {
         $(
" MultiUploadFile " ).content.JavaScriptObject.UploadAttchmentList  =  getAttachmentList;         
     }
 }
 
// 获取silverlight插件已经上传的附件列表
function  getAttachmentList(sender, args) {
     
var  attachment  =  args.AttchmentList;
     
if  (isUndefined(attachment)  ||  attachment  ==   ' [] ' ) {
         BOX_remove(
' silverlightControlHost ' );
         
return ;
     }
     
var  attachmentList  =  eval( " ( "   +  attachment  +   " ) " );

     BOX_remove(
' silverlightControlHost ' );
     addAttachUploaded(attachmentList);     
 }

 
         这样一个SL多文件上传插件就算基本完成了。当然我还写了“缩略图”功能,因为代码很简单就不多说了。


本文转自 daizhenjun 51CTO博客,原文链接:http://blog.51cto.com/daizhj/147173,如需转载请自行联系原作者
相关文章
|
开发框架 移动开发