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

本文详细介绍了一个使用SL实现的多文件上传插件的设计与实现过程,包括文件选择、分块上传、进度显示及最终上传等功能。插件通过严格的文件大小、类型检查确保上传合规,并在上传过程中实时统计已上传文件的总大小。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  如果当前用户通过验证,就可以通过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 / 10242)
                 
+ " MB, 而今天还可以上传:" + Math.Round((decimal)(_todayAttachSize - _todayUploadSize) / 1024 / 10242+ " MB.");
                
break;
            }
            
//当单个文件大小大于最大上传附件尺寸时
            if (userFile.FileStream.Length > _maxFileSize)
            {
                ShowMessageBox(
"\r\n当前附件大小: " + Math.Round((decimal)userFile.FileStream.Length / 1024 / 10242)
                
+ " MB, 而单个附件允许最大尺寸为: " + Math.Round((decimal)_maxFileSize / 1024 / 10242+ " 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(10009999
                       
+ "." + 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 
- 11);

    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,如需转载请自行联系原作者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值