zoukankan      html  css  js  c++  java
  • Silverlight多文件(大文件)上传的开源项目

    在Silverlight上实现文件上传的例子在网上的还不多,特别是多文件上传和大文件上传的例子就更少了。当然
    那些商品软件公司的产品除外。

         目前的CodePlex上就有这样一个项目,其链接:http://www.codeplex.com/SLFileUpload/ ,他的个人主
    站链接:http://www.michielpost.nl/    
        
         我在本地下载运行其代码后,发现“果然”很好用,而且代码写的也很规范。当然其也是免费的,但作者并不
    绝各种名义上的“捐助(Donate)”。

        下面就是其“汉化”后的运行截图,首先是多文件上传
        
                  
       

         然后是大文件上传:
        
                  

        根据作者的README文件,其支持下面几个初始化参数:    
       

        MaxFileSizeKB:  File size in KBs.
        MaxUploads:  Maximum number of simultaneous uploads
        FileFilter: File filter, for example ony jpeg use: FileFilter=Jpeg (*.jpg) |*.jpg
        CustomParam: Your custom parameter, anything here will be available in the WCF webservice
        DefaultColor: The default color for the control, for example: LightBlue

        
        当然,里面的服务端采用WCF方法。为了考虑在.net1框架上也可以使用,我在保留原有代码结构的基础上,将WCF 
    用ASMX格式拷贝了一份,经过编译,完成可以运行:)

        同时为了便于大家阅读源码,我还加入了中文说明(源码中注释很少,而且是EN文)。下面就是其主要的几个类的
    定义和说明:

        FileCollection 上传文件集合类,用于UI统一访问和操作:
        


    /// <summary>
    /// 文件集合管理类
    /// 注:ObservableCollection是个泛型集合类,往其中添加或去除条目时(或者其中的条目实现了INotifyPropertyChanged的话,在属性变动时),
    /// 它会发出变化通知事件(先执行集合类中的同名属性)。这在做数据绑定时会非常方便,因为UI控件可以使用这些通知来知道自动刷新它们的值,
    /// 而不用开发人员编写代码来显式地这么做。
    /// </summary>
    public class FileCollection : ObservableCollection<UserFile>
    {
        
    /// <summary>
        
    /// 已上传的累计(多文件)字节数
        
    /// </summary>
        private double _bytesUploaded = 0;
        
    /// <summary>
        
    /// 已上传字符数占全部字节数的百分比
        
    /// </summary>
        private int _percentage = 0;
        
    /// <summary>
        
    /// 当前正在上传的文件序号
        
    /// </summary>
        private int _currentUpload = 0;
        
    /// <summary>
        
    /// 上传初始化参数,详情如下:
        
    /// MaxFileSizeKB:  File size in KBs.
        
    /// MaxUploads:  Maximum number of simultaneous uploads
        
    /// FileFilter: File filter, for example ony jpeg use: FileFilter=Jpeg (*.jpg) |*.jpg
        
    /// CustomParam: Your custom parameter, anything here will be available in the WCF webservice
        
    /// DefaultColor: The default color for the control, for example: LightBlue
        
    /// </summary>
        private string _customParams;
        
    /// <summary>
        
    /// 最大上传字节数
        
    /// </summary>
        private int _maxUpload;
        
        
    /// <summary>
        
    /// 已上传的累计(多文件)字节数,该字段的修改事件通知会发给page.xmal中的TotalKB
        
    /// </summary>
        public double BytesUploaded
        {
            
    get { return _bytesUploaded; }
            
    set
            {
                _bytesUploaded 
    = value;
                
    this.OnPropertyChanged(new PropertyChangedEventArgs("BytesUploaded"));
            }
        }

        
    /// <summary>
        
    /// 已上传字符数占全部字节数的百分比,该字段的修改事件通知会发给page.xmal中的TotalProgress
        
    /// </summary>
        public int Percentage
        {
            
    get { return _percentage; }
            
    set
            {
                _percentage 
    = value;
                
    this.OnPropertyChanged(new PropertyChangedEventArgs("Percentage"));
            }
        }

        
    /// <summary>
        
    /// 构造方法
        
    /// </summary>
        
    /// <param name="customParams"></param>
        
    /// <param name="maxUploads"></param>
        public FileCollection(string customParams, int maxUploads)
        {
            _customParams 
    = customParams;
            _maxUpload 
    = maxUploads;

            
    this.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FileCollection_CollectionChanged);
        }

       
        
    /// <summary>
        
    /// 依次加入所选的上传文件信息
        
    /// </summary>
        
    /// <param name="item"></param>
        public new void Add(UserFile item)
        {
            item.PropertyChanged 
    += new PropertyChangedEventHandler(item_PropertyChanged);            
            
    base.Add(item);
        }

        
    /// <summary>
        
    /// 单个上传文件属性改变时
        
    /// </summary>
        
    /// <param name="sender"></param>
        
    /// <param name="e"></param>
        void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            
    //当属性变化为“从上传列表中移除”
            if (e.PropertyName == "IsDeleted")
            {
                UserFile file 
    = (UserFile)sender;

                
    if (file.IsDeleted)
                {
                    
    if (file.State == Constants.FileStates.Uploading)
                    {
                        _currentUpload
    --;
                        UploadFiles();
                    }

                    
    this.Remove(file);

                    file 
    = null;
                }
            }
            
    //当属性变化为“开始上传”
            else if (e.PropertyName == "State")
            {
                UserFile file 
    = (UserFile)sender;
                
    //此时file.State状态为ploading
                if (file.State == Constants.FileStates.Finished || file.State == Constants.FileStates.Error)
                {
                    _currentUpload
    --;
                    UploadFiles();
                }
            }
            
    //当属性变化为“上传进行中”
            else if (e.PropertyName == "BytesUploaded")
            {
                
    //重新计算上传数据
                RecountTotal();
            }
        }
     

        
    /// <summary>
        
    /// 上传文件
        
    /// </summary>
        public void UploadFiles()
        {
            
    lock (this)
            {
                
    foreach (UserFile file in this)
                {   
    //当上传文件未被移除(IsDeleted)或是暂停时
                    if (!file.IsDeleted && file.State == Constants.FileStates.Pending && _currentUpload < _maxUpload)
                    {
                        file.Upload(_customParams);
                        _currentUpload
    ++;
                    }
                }
            }

        }

        
    /// <summary>
        
    /// 重新计算数据
        
    /// </summary>
        private void RecountTotal()
        {
            
    //Recount total
            double totalSize = 0;
            
    double totalSizeDone = 0;

            
    foreach (UserFile file in this)
            {
                totalSize 
    += file.FileSize;
                totalSizeDone 
    += file.BytesUploaded;
            }

            
    double percentage = 0;

            
    if (totalSize > 0)
                percentage 
    = 100 * totalSizeDone / totalSize;

            BytesUploaded 
    = totalSizeDone; 

            Percentage 
    = (int)percentage;
        }

        
    /// <summary>
        
    /// 当添加或取消上传文件时触发
        
    /// </summary>
        
    /// <param name="sender"></param>
        
    /// <param name="e"></param>
        void FileCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            
    //当集合信息变化时(添加或删除项)时,则重新计算数据 
            RecountTotal();
        }
    }

        上传文件信息类:
        


    /// <summary>
    /// 上传文件信息类
    /// </summary>
    public class UserFile : INotifyPropertyChanged
    {
        
    /// <summary>
        
    /// 上传文件名称
        
    /// </summary>
        private string _fileName;
        
    /// <summary>
        
    /// 是否取消上传该文件
        
    /// </summary>
        private bool _isDeleted = false;      
        
    /// <summary>
        
    /// 上传文件的流信息
        
    /// </summary>
        private Stream _fileStream;
        
    /// <summary>
        
    /// 当前上传文件状态
        
    /// </summary>
        private Constants.FileStates _state = Constants.FileStates.Pending;
        
    /// <summary>
        
    /// 当前已上传的字节数(这里与FileCollection中的同名属性意义不同,FileCollection中的是已上传的所有文件的字节总数)
        
    /// </summary>
        private double _bytesUploaded = 0;
        
    /// <summary>
        
    /// 当前文件大小
        
    /// </summary>
        private double _fileSize = 0;
        
    /// <summary>
        
    /// 已上传文件的百分比
        
    /// </summary>
        private int _percentage = 0;
        
    /// <summary>
        
    /// 上传文件操作类
        
    /// </summary>
        private FileUploader _fileUploader;

        
    /// <summary>
        
    /// 上传文件名称
        
    /// </summary>
        public string FileName
        {
            
    get { return _fileName; }
            
    set
            {
                _fileName 
    = value;
                NotifyPropertyChanged(
    "FileName");
            }
        }

        
    /// <summary>
        
    /// 当前上传文件的状态,注意这时使用了NotifyPropertyChanged来通知FileRowControl控件中的FileRowControl_PropertyChanged事件
        
    /// </summary>
        public Constants.FileStates State
        {
            
    get { return _state; }
            
    set
            {
                _state 
    = value;

                NotifyPropertyChanged(
    "State");
            }
        }

        
    /// <summary>
        
    /// 当前上传文件是否已被移除,注意这时使用了NotifyPropertyChanged来通知FileCollection类中的item_PropertyChanged事件
        
    /// </summary>
        public bool IsDeleted
        {
            
    get { return _isDeleted; }
            
    set
            {
                _isDeleted 
    = value;

                
    if (_isDeleted)
                    CancelUpload();

                NotifyPropertyChanged(
    "IsDeleted");
            }
        }

        
    /// <summary>
        
    /// 上传文件的流信息
        
    /// </summary>
        public Stream FileStream
        {
            
    get { return _fileStream; }
            
    set
            {
                _fileStream 
    = value;

                
    if (_fileStream != null)
                    _fileSize 
    = _fileStream.Length;                
                
            }
        }

        
    /// <summary>
        
    /// 当前文件大小
        
    /// </summary>
        public double FileSize
        {
            
    get {
                
    return _fileSize;               
            }
        }

        
    /// <summary>
        
    /// 当前已上传的字节数(这里与FileCollection中的同名属性意义不同,FileCollection中的是已上传的所有文件的字节总数)
        
    /// </summary>
        public double BytesUploaded
        {
            
    get { return _bytesUploaded; }
            
    set
            {
                _bytesUploaded 
    = value;

                NotifyPropertyChanged(
    "BytesUploaded");

                Percentage 
    = (int)((value * 100/ _fileStream.Length);

            }
        }

        
    /// <summary>
        
    /// 已上传文件的百分比(这里与FileCollection中的同名属性意义不同,FileCollection中的是已上传字符数占全部字节数的百分比,该字段的修改事件通知会发给page.xmal中的TotalProgress)
        
    /// </summary>
        public int Percentage
        {
            
    get { return _percentage; }
            
    set
            {
                _percentage 
    = value;
                NotifyPropertyChanged(
    "Percentage");
            }
        }

      
        
    /// <summary>
        
    /// 上传当前文件
        
    /// </summary>
        
    /// <param name="initParams"></param>
        public void Upload(string initParams)
        {
            
    this.State = Constants.FileStates.Uploading;

            _fileUploader 
    = new FileUploader(this);            
            _fileUploader.UploadAdvanced(initParams);
            _fileUploader.UploadFinished 
    += new EventHandler(fileUploader_UploadFinished);            
        }

        
    /// <summary>
        
    /// 取消上传,注:该文件仅在本类中的IsDeleted属性中使用
        
    /// </summary>
        public void CancelUpload()
        {
            
    if (_fileUploader != null && this.State == Constants.FileStates.Uploading)
            {
                _fileUploader.CancelUpload();
            }
        }

        
    /// <summary>
        
    /// 当前文件上传完成时
        
    /// </summary>
        
    /// <param name="sender"></param>
        
    /// <param name="e"></param>
        void fileUploader_UploadFinished(object sender, EventArgs e)
        {
            _fileUploader 
    = null;

            
    this.State = Constants.FileStates.Finished;
        }


        
    #region INotifyPropertyChanged Members

        
    private void NotifyPropertyChanged(string prop)
        {
            
    if (PropertyChanged != null)
            {
                PropertyChanged(
    thisnew PropertyChangedEventArgs(prop));
            }
        }

        
    public event PropertyChangedEventHandler PropertyChanged;

        
    #endregion
    }

          
        上传文件操作类(实现文件上传功能代码):    

     


    /// <summary>
    /// 文件上传类
    /// </summary>
    public class FileUploader
    {
        
    private UserFile _file;
        
    private long _dataLength;
        
    private long _dataSent;
        
    private SilverlightUploadServiceSoapClient _client;
        
    private string _initParams;
        
    private bool _firstChunk = true;
        
    private bool _lastChunk = false;
        

        
    public FileUploader(UserFile file)
        {
            _file 
    = file;

            _dataLength 
    = _file.FileStream.Length;
            _dataSent 
    = 0;

            
    //创建WCF端,此处被注释
            
    //BasicHttpBinding binding = new BasicHttpBinding();
            
    //EndpointAddress address = new EndpointAddress(new CustomUri("SilverlightUploadService.svc"));
            
    //_client = new UploadService.UploadServiceClient(binding, address);
            
    //_client = new UploadService.UploadServiceClient();
            
    //_client.InnerChannel.Closed += new EventHandler(InnerChannel_Closed);
            
            
    //创建webservice客户端
            _client = new SilverlightUploadServiceSoapClient();
            
    //事件绑定
            _client.StoreFileAdvancedCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(_client_StoreFileAdvancedCompleted);
            _client.CancelUploadCompleted 
    += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(_client_CancelUploadCompleted);
            _client.ChannelFactory.Closed 
    += new EventHandler(ChannelFactory_Closed);
        }

        
    #region
        
    /// <summary>
        
    /// 关闭ChannelFactory事件
        
    /// </summary>
        
    /// <param name="sender"></param>
        
    /// <param name="e"></param>
        void ChannelFactory_Closed(object sender, EventArgs e)
        {
            ChannelIsClosed();
        }

        
    void _client_CancelUploadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            
    //当取消上传完成后关闭Channel
            _client.ChannelFactory.Close();
        }

        
    /// <summary>
        
    /// Channel被关闭
        
    /// </summary>
        private void ChannelIsClosed()
        {
            
    if (!_file.IsDeleted)
            {
                
    if (UploadFinished != null)
                    UploadFinished(
    thisnull);
            }
        }

        
    /// <summary>
        
    /// 取消上传
        
    /// </summary>
        public void CancelUpload()
        {
            _client.CancelUploadAsync(_file.FileName);
        }
        
    #endregion

        
    /// <summary>
        
    /// 上传完成事件处理对象声明
        
    /// </summary>
        public event EventHandler UploadFinished;

        
    public void UploadAdvanced(string initParams)
        {
            _initParams 
    = initParams;

            UploadAdvanced();
        }

        
    /// <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);


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

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

                _client.ChannelFactory.Close();          
            }

        }

        
    /// <summary>
        
    /// 修改进度属性
        
    /// </summary>
        private void OnProgressChanged()
        {
            _file.BytesUploaded 
    = _dataSent;//注:此处会先调用FileCollection中的同名属性,然后才是_file.BytesUploaded属性绑定
        }

        
    void _client_StoreFileAdvancedCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            
    //检查WEB服务是否存在错误
            if (e.Error != null)
            {
                
    //当错误时放弃上传
                _file.State = Constants.FileStates.Error;
            }
            
    else
            {
                
    //如果文件未取消上传的话,则继续上传
                if (!_file.IsDeleted)
                    UploadAdvanced();
            }
        }

    }


        服务端WCF代码如下(ASMX文件代码与其基本相同):    


    [AspNetCompatibilityRequirements  (RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class UploadService : IUploadService
    {
        
    private string _tempExtension = "_temp";

        
    #region IUploadService Members

        
        
    /// <summary>
        
    /// 取消上传
        
    /// </summary>
        
    /// <param name="fileName"></param>
        public void CancelUpload(string fileName)
        {
            
    string uploadFolder = GetUploadFolder();
            
    string tempFileName = fileName + _tempExtension;

            
    if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName))
                File.Delete(@HostingEnvironment.ApplicationPhysicalPath 
    + "/" + uploadFolder + "/" + tempFileName);
        }

        
    public void StoreFileAdvanced(string fileName, byte[] data, int dataLength, string parameters, bool firstChunk, bool lastChunk)
        {
            
    string uploadFolder = GetUploadFolder();
            
    string tempFileName = fileName + _tempExtension;

            
    //当上传文件的第一批数据时,先清空以往的相同文件名的文件(同名文件可能为上传失败造成)
            if (firstChunk)
            {
                
    //删除临时文件
                if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName))
                    File.Delete(@HostingEnvironment.ApplicationPhysicalPath 
    + "/" + uploadFolder + "/" + tempFileName);

                
    //删除目标文件
                if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName))
                    File.Delete(@HostingEnvironment.ApplicationPhysicalPath 
    + "/" + uploadFolder + "/" + fileName);

            }


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

            
    if (lastChunk)
            {
                
    //将临时文件重命名为原来的文件名称
                File.Move(HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + tempFileName, HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName);

                
    //Finish stuff.
                FinishedFileUpload(fileName, parameters);
            }

        }

        
    /// <summary>
        
    /// 删除上传文件
        
    /// </summary>
        
    /// <param name="fileName"></param>
        protected void DeleteUploadedFile(string fileName)
        {
            
    string uploadFolder = GetUploadFolder();

            
    if (File.Exists(@HostingEnvironment.ApplicationPhysicalPath + "/" + uploadFolder + "/" + fileName))
                File.Delete(@HostingEnvironment.ApplicationPhysicalPath 
    + "/" + uploadFolder + "/" + fileName);
        }

        
    protected virtual void FinishedFileUpload(string fileName, string parameters)
        {
        }

        
    /// <summary>
        
    /// 获取上传路径
        
    /// </summary>
        
    /// <returns></returns>
        protected virtual string GetUploadFolder()
        {
            
    return "Upload";
        }     
        
    #endregion
    }

     

        当然在该DEMO中,其支持两种初始化方式,一种是:

    <asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/mpost.SilverlightMultiFileUpload.xap" MinimumVersion="2.0.30523"  Width="415" Height="280"   InitParameters="MaxFileSizeKB=1000,MaxUploads=2,FileFilter=,CustomParam=1,DefaultColor=LightBlue"  />

     
        另一种是在ServiceReferences.ClientConfig中进行文件配置:    
       

    <appSettings>
           
    <add key="MaxFileSizeKB" value="50" />
           
    <add key="FileFilter" value="Photo's (*.jpg)|*.jpg" />
           
    <add key="FileFilter" value="" />
           
    <add key="MaxUploads" value="2" />
    </appSettings>

        
        而加载顺序要是自上而下,代码段如下(摘自Page.xaml.cs):    


    /// <summary>
    /// 加载配置参数 then from .Config file
    /// </summary>
    /// <param name="initParams"></param>
    private void LoadConfiguration(IDictionary<stringstring> initParams)
    {
        
    string tryTest = string.Empty;

        
    //加载定制配置信息串
        if (initParams.ContainsKey("CustomParam"&& !string.IsNullOrEmpty(initParams["CustomParam"]))
            _customParams 
    = initParams["CustomParam"];

        
    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 (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["MaxFileSizeKB"]))
        {
            
    if (int.TryParse(ConfigurationManager.AppSettings["MaxFileSizeKB"], out _maxFileSize))
                _maxFileSize 
    = _maxFileSize * 1024;
        }

        
        
    if(!string.IsNullOrEmpty(ConfigurationManager.AppSettings["MaxUploads"]))
            
    int.TryParse(ConfigurationManager.AppSettings["MaxUploads"], out _maxUpload);

        
    if(!string.IsNullOrEmpty( ConfigurationManager.AppSettings["FileFilter"]))
            _fileFilter 
    = ConfigurationManager.AppSettings["FileFilter"];

    }


              
        好了,今天的内容就先到这里了,感兴趣的朋友可以在回复中进行讨论或给他(作者)留言,
    contact@MichielPost.nl 
        
       

        作者:代震军,daizhj
        
        tags:silverlight,uploade, 文件上传, 多文件,大文件
        
        中文注释的源码下载,请点击这里。 
        
        CodePlex, 下载链接:)
     

    Powered By D&J (URL:http://www.cnblogs.com/Areas/)
  • 相关阅读:
    css实现文字渐变
    mySql中Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre的问题
    mysql不等于判断时,空值过滤问题
    SpringBoot中maven打包,启动报没有主清单属性
    MySQL插入数据时报错Cause: java.sql.SQLException: #HY000的解决方法
    SpringCloud中Feign服务调用请求方式及参数总结
    linux查看占用端口号的程序及pid
    Ant Design Pro 改变默认启动端口号
    启动jar包并生成日志的linux脚本
    JavaWeb中验证码校验的功能实现
  • 原文地址:https://www.cnblogs.com/Areas/p/2176460.html
Copyright © 2011-2022 走看看