博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Silverlight多文件(大文件)上传的开源项目
阅读量:6975 次
发布时间:2019-06-27

本文共 15366 字,大约阅读时间需要 51 分钟。

在Silverlight上实现文件上传的例子在网上的还不多,特别是多文件上传和大文件上传的例子就更少了。当然

那些商品软件公司的产品除外。

     目前的CodePlex上就有这样一个项目,其链接: ,他的个人主

站链接:    
    
     我在本地下载运行其代码后,发现“果然”很好用,而且代码写的也很规范。当然其也是免费的,但作者并不
绝各种名义上的“捐助(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统一访问和操作:

    

ExpandedBlockStart.gif
/// <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();
    }
}

    上传文件信息类:

    

ExpandedBlockStart.gif
/// <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
}

      

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

 

ExpandedBlockStart.gif
/// <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文件代码与其基本相同):    

ExpandedBlockStart.gif
[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):    

ExpandedBlockStart.gif
/// <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"];
}

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

    作者:代震军,daizhj

    
    tags:silverlight,uploade, 文件上传, 多文件,大文件
    
    中文注释的源码下载,请。 
    
    CodePlex, :) 

你可能感兴趣的文章
2015年的智能大招是怎么憋出来的?
查看>>
2.5万名网友认同流量跑得快 电信计量监管陷尴尬局面
查看>>
你未必知道 十大服务器虚拟化优化窍门
查看>>
POTN——新时代网络融合的必经之路
查看>>
浅谈各地降低能耗技术 关注可持续发展
查看>>
如何写出漂亮的React组件
查看>>
Windows管理员不可错过的那些卓越DevOps工具(下)
查看>>
物联网兴起 我国工业发展需关注四大关键技术
查看>>
面向未来就绪,企业级存储如何重新定义?
查看>>
智慧医疗在张家界的应用 首家智慧医疗医院投用
查看>>
Web 开发中 20 个很有用的 CSS 库
查看>>
Android系统中的进程管理:进程的创建
查看>>
看好大工业数据市场 GE将向软件业务14亿美元
查看>>
《JavaScript和jQuery实战手册(原书第2版)》——1.1节编程简介
查看>>
牛津大学量化金融创始人:如何获取并应用互联网大数据?
查看>>
快讯:特斯拉宣布26亿美元收购太阳能公司SolarCity
查看>>
你要知道的4个机房除尘小技巧
查看>>
百度金融布局金交所,账户和场景成掣肘
查看>>
仙童半导体拒绝华润等收购 担忧难获监管批准
查看>>
阿里视频云最强转码技术揭秘:窄带高清原理解析+用户接入指南
查看>>