微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

【分享】 在silverlight中使用wcf上传文件并实时显示进度

公司一个项目要用到文件上件, Google后了解到上传一般有三种方式:WebClient、WebService和WCF。

最开始用WebClient.OpenWrite实现了文件上传,但弄不明白怎么才能得到上传进度,只好放弃了。

用WebService还不如直接WCF,参考http://www.cnblogs.com/blackcore/archive/2009/11/21/1607823.html后,自己做了一个,最终的效果图如下:



简单实现步骤如下:

一、在sl.Web项目中添加“启用了 Silverlight 的 WCF 服务”。WCF服务的内容十分简单,只需一个功能:接收字节并储存即可。

        代码如下:

    [ServiceContract(Namespace = "")]
    [SilverlightFaultBehavior]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class UploadFileWCFService
    {
        [OperationContract]
        public void DoWork()
        {
            // 在此处添加操作实现
            return;
        }

        /// <summary>
        /// 开始上传。需要在WCF服务启动文件夹下建立UploadFiles、TempuploadFolder文件夹。
        /// UploadFiles储存上传文件夹
        /// TempuploadFolder为上传临时文件夹
        /// </summary>
        /// <param name="FullPath">保存路径及文件名</param>
        /// <param name="FileContext">文件内容</param>
        /// <param name="Overwrite">是否覆盖</param>
        [OperationContract]
        public void BeginUpload(string FullPath,byte[] FileContext,bool Overwrite)
        {
            try
            {
                if (!FullPath.StartsWith("\\"))
                {
                    FullPath = "\\" + FullPath;
                }           
                string defaultFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/UploadFiles");
                string tempuploadFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/TempuploadFolder");
                string SaveFolder = Path.GetDirectoryName(FullPath);
                string Filename = Path.GetFileName(FullPath);
                string BlockString = Path.GetExtension(Filename);
                string[] block = BlockString.Split('-');
                if (block.Length != 2)
                {
                    throw new Exception("系统不支持此操作");
                }
                int FilesIndex = Convert.ToInt32(block[0].Replace(".",""));
                int FilesCount = Convert.ToInt32(block[1]);
                string tempFolder = tempuploadFolder + "\\" + Path.GetFileNameWithoutExtension(FullPath); //上传到临时文件夹 

                Directory.CreateDirectory(tempFolder);
                if (!Directory.Exists(defaultFolder + "\\" + SaveFolder))
                {
                    Directory.CreateDirectory(defaultFolder + "\\" + SaveFolder);
                }
                FileMode mode = FileMode.Create;
                if (!Overwrite)
                {
                    mode = FileMode.Append;
                }
                using (FileStream fs = new FileStream(tempFolder + "\\" + Filename,mode,FileAccess.Write))
                {
                    fs.Write(FileContext,FileContext.Length);
                }
                if (FilesIndex == FilesCount - 1)//是否最后一块文件,如是则合并文件块得到完整文件
                {
                    string UploadFile = defaultFolder + "\\" + SaveFolder + "\\" + Path.GetFileNameWithoutExtension(FullPath);
                    string[] files = Directory.GetFiles(tempFolder);
                    using (FileStream sFile = new FileStream(UploadFile,FileMode.Create,FileAccess.Write))
                    {
                        foreach (string file in files)
                        {
                            using (FileStream fs = new FileStream(file,FileMode.Open,FileAccess.Read))
                            {
                                byte[] context = new byte[fs.Length];
                                fs.Read(context,context.Length);
                                sFile.Write(context,context.Length);
                            }
                            File.Delete(file);
                        }
                    }
                    Directory.Delete(tempFolder);
                }
            }
            catch (Exception)
            {
                throw;
            }
            return;
        }
    }

二、为让WCF服务支持文件需对sl.Web项目中的web.config进行配置,增加MaxArrayLength属性的设置:

  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <customBinding>
        <binding name="AutoloanMobileManager.Web.Service.UploadFileWCFService.customBinding0">
          <binaryMessageEncoding>
          <readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"
              maxBytesPerRead="2147483647" />
          </binaryMessageEncoding>
          <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
        </binding>
      </customBinding>
    </bindings>
    <services>
      <service name="AutoloanMobileManager.Web.Service.UploadFileWCFService">
        <endpoint address="" binding="customBinding" bindingConfiguration="AutoloanMobileManager.Web.Service.UploadFileWCFService.customBinding0"
          contract="AutoloanMobileManager.Web.Service.UploadFileWCFService" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
      multipleSiteBindingsEnabled="true" />

三、在sl项目中添加上传类,实现上传方法

       利用WCF异步调用的特点,将要上传文件分成N个文件块依次上传,由此获得准确的上传进度。代码如下:

   public class WcfUploadFileService : INotifyPropertyChanged
    {

        public class FileList : INotifyPropertyChanged
        {
            private void NotifyPropertyChanged(string info)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this,new PropertyChangedEventArgs(info));
                }
            }

            private int m_BufferSize = 4096;
            private FileInfo m_FileList;
            private UploadStatusType m_UploadStauts;
            private long m_BytesSent;
            private long m_BytesSentCount;
            private int m_SentPercentage;
            private string m_SavePath;
            private ObservableCollection<UploadLog> m_LOG = new ObservableCollection<UploadLog>();

            public enum UploadStatusType
            {
                Waitting,Uploading,Completed,Failed,Pause
            }

            /// <summary>
            /// 每次上传的字节数。应在给File赋值前设定。
            /// </summary>
            public int BufferSize
            {
                get
                {
                    return m_BufferSize;
                }
                set
                {
                    m_BufferSize = value;
                }
            }

            public class UploadLog
            {
                public UploadLog(DateTime time,string context,object tag)
                {
                    LogTime = time;
                    LogContext = context;
                    Tag = tag;
                }

                public DateTime LogTime { get; set; }
                public string LogContext { get; set; }
                public object Tag { get; set; }
            }

            /// <summary>
            /// 上传日志
            /// </summary>
            public ObservableCollection<UploadLog> LOG
            {
                get
                {
                    return m_LOG;
                }
                set
                {
                    m_LOG = value;
                    NotifyPropertyChanged("LOG");
                }
            }

            private void AddLog(UploadLog log)
            {
                LOG.Add(log);
            }

            /// <summary>
            /// 等待传输的字节数据列表。设计this.File时自动赋值,按BufferSize读取至Byte[]等待上传。
            /// </summary>
            public List<Byte[]> FileContext { get; set; }

            /// <summary>
            /// 要上传文件
            /// </summary>
            public FileInfo File
            {
                get { return m_FileList; }
                set
                {
                    m_FileList = value;
                    Stream soureFile = null;
                    UploadStatus = UploadStatusType.Waitting;
                    try
                    {
                        soureFile = value.OpenRead();
                    }
                    catch (Exception)
                    {
                        UploadStatus = UploadStatusType.Failed;
                        AddLog(new UploadLog(DateTime.Now,"无法读取文件",null));
                    }
                    long BytesCount = soureFile.Length;
                    long BytesRead = 0;
                    if (FileContext == null)
                    {
                        FileContext = new List<byte[]>();
                    }
                    else
                    {
                        FileContext.Clear();
                    }
                    long ReadSize = BufferSize;
                    while (BytesRead < BytesCount)
                    {
                        if (BytesRead + BufferSize > BytesCount) //调整最后一个文件块的大小
                        {
                            ReadSize = BytesCount - BytesRead;
                        }
                        byte[] bytes = new byte[ReadSize];
                        BytesRead += soureFile.Read(bytes,bytes.Length);
                        FileContext.Add(bytes);
                    }
                    soureFile.Close();
                    soureFile.dispose();
                    NotifyPropertyChanged("File");
                }
            }

            /// <summary>
            /// 保存路径
            /// </summary>
            public string SavePath
            {
                get { return m_SavePath; }
                set { m_SavePath = value; NotifyPropertyChanged("SavePath"); }
            }

            /// <summary>
            /// 上传状态
            /// </summary>
            public UploadStatusType UploadStatus
            {
                get { return m_UploadStauts; }
                set { m_UploadStauts = value; NotifyPropertyChanged("UploadStatus"); }
            }
            /// <summary>
            /// 本次上传字节
            /// </summary>
            public long BytesSent
            {
                get { return m_BytesSent; }
                set { m_BytesSent = value; NotifyPropertyChanged("BytesSent"); }
            }
            /// <summary>
            /// 已上传字节
            /// </summary>
            public long BytesSentCount
            {
                get { return m_BytesSentCount; }
                set { m_BytesSentCount = value; NotifyPropertyChanged("BytesSentCount"); }
            }
            /// <summary>
            /// 已上传比率
            /// </summary>
            public int SentPercentage
            {
                get { return m_SentPercentage; }
                set { m_SentPercentage = value; NotifyPropertyChanged("SentPercentage"); }
            }

            #region INotifyPropertyChanged 成员

            public event PropertyChangedEventHandler PropertyChanged;

            #endregion
        }

        UploadFileWCFServiceClient uploadClient = new UploadFileWCFServiceClient();

        public WcfUploadFileService()
        {
            uploadClient.BeginUploadCompleted += new EventHandler<AsyncCompletedEventArgs>(uploadClient_BeginUploadCompleted);
        }

        void uploadClient_BeginUploadCompleted(object sender,AsyncCompletedEventArgs e)
        {
            //通过CurrentFileIndex 、CurrentFileContextIndex控制下一个上传的块
            bool beginNew = false; //是开始一个文件或续传
            if (e.Error != null)
            {
                //如错误,放弃当前文件,传下一文件
                Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Failed;
                Files[CurrentFileIndex].LOG.Add(new FileList.UploadLog(DateTime.Now,e.Error.Message,null));
                CurrentFileIndex++;
                CurrentFileContextIndex = 0;
                beginNew = true;
            }
            else
            {
                Files[CurrentFileIndex].BytesSent = Files[CurrentFileIndex].FileContext[CurrentFileContextIndex].Length;
                Files[CurrentFileIndex].BytesSentCount += Files[CurrentFileIndex].BytesSent;
                Files[CurrentFileIndex].SentPercentage = Convert.ToInt32((double)Files[CurrentFileIndex].BytesSentCount / (double)Files[CurrentFileIndex].File.Length * 100);
                //计算下一个上传文件号和块号
                if (CurrentFileIndex < Files.Count)
                {
                    if (CurrentFileContextIndex < Files[CurrentFileIndex].FileContext.Count - 1)
                    {
                        CurrentFileContextIndex++;
                    }
                    else
                    {
                        Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Completed;
                        CurrentFileIndex++;
                        CurrentFileContextIndex = 0;
                        beginNew = true;
                    }
                }
            }
            if (CurrentFileIndex < Files.Count)
            {
                Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Uploading;
                uploadClient.BeginUploadAsync(Files[CurrentFileIndex].SavePath + "//"
                    + Files[CurrentFileIndex].File.Name + "." 
                    + CurrentFileContextIndex.ToString().PadLeft(9,'0')
                    + "-" + Files[CurrentFileIndex].FileContext.Count,Files[CurrentFileIndex].FileContext[CurrentFileContextIndex],beginNew);
            }
            else
            {
                Files[CurrentFileIndex - 1].UploadStatus = FileList.UploadStatusType.Completed;
            }
        }

        private ObservableCollection<FileList> m_Files = new ObservableCollection<FileList>();

        /// <summary>
        /// 准备上传文件列表
        /// </summary>
        public ObservableCollection<FileList> Files
        {
            get
            {
                return m_Files;
            }
            set
            {
                m_Files = value;
                NotifyPropertyChanged("Files");
            }
        }

        /// <summary>
        /// 当前上传文件索引
        /// </summary>
        private int CurrentFileIndex;
        /// <summary>
        /// 当前上传文件块索引
        /// </summary>
        private int CurrentFileContextIndex;

        /// <summary>
        /// 开始上传。按BufferSize将文件分块,块命名规则为 filename.blockid-blockcount,服务器接收完最后一块后合并成源文件。
        /// </summary>
        public void BeginUpload()
        {
            if (Files.Count > 0)
            {
                CurrentFileIndex = 0;
                CurrentFileContextIndex = 0;
                Files[CurrentFileIndex].UploadStatus = FileList.UploadStatusType.Uploading;
                uploadClient.BeginUploadAsync(Files[CurrentFileIndex].SavePath + "//"
                    + Files[CurrentFileIndex].File.Name + "."
                    + CurrentFileContextIndex.ToString().PadLeft(9,true);
            }
        }

        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,new PropertyChangedEventArgs(info));
            }
        }

        #endregion

四、制作简单的上传控件,代码如下:


<UserControl x:Class="AutoloanMobileManager.Controls.UploadFileControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
        <Grid>
            <Grid.ColumnDeFinitions>
                <ColumnDeFinition Width="70"/>
                <ColumnDeFinition Width="*"/>
            </Grid.ColumnDeFinitions>
        <ProgressBar x:Name="uploadProgress" Value="{Binding SentPercentage}" Height="20" 
                     Background="Yellow" Width="400" Grid.Column="1"/>
        <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right">
            <TextBlock Text="{Binding BytesSentCount}" VerticalAlignment="Center"/>
            <TextBlock Text="/" VerticalAlignment="Center"/>
            <TextBlock Text="{Binding File.Length}" VerticalAlignment="Center"/>
        </StackPanel>
        <TextBlock Text="{Binding File.Name}" Grid.Column="1" HorizontalAlignment="Left" 
                   VerticalAlignment="Center" Foreground="DarkBlue"
                   Margin="8,0"/>
            <TextBlock Text="{Binding UploadStatus}" Grid.Column="0" VerticalAlignment="Center"/>
        </Grid>
</UserControl>

五、新建一个Page,前台界面如下:

        注意添加第四步中定义的UploadFileControl的引用

<navigation:Page x:Class="AutoloanMobileManager.Pages.UploadFilePage" 
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:control="clr-namespace:AutoloanMobileManager.Controls"
           mc:Ignorable="d"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           d:DesignWidth="640" d:DesignHeight="480"
           Title="UploadFilePage Page">
    <StackPanel>
        <ListBox Margin="10" x:Name="list_uploadlist">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <control:UploadFileControl/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Content="选择文件" Click="Button_Click" Width="80" HorizontalAlignment="Left" Margin="5"/>
    </StackPanel>
</navigation:Page>

后台代码

public partial class UploadFilePage : Page
    {
        WcfUploadFileService upload = new WcfUploadFileService();

        public UploadFilePage()
        {
            InitializeComponent();
            list_uploadlist.ItemsSource = upload.Files;
        }

        // 当用户导航到此页面时执行。
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void Button_Click(object sender,RoutedEventArgs e)
        {
            OpenFileDialog file = new OpenFileDialog();
            file.Multiselect = true;
            bool? result = file.ShowDialog();
            if (result.HasValue)
            {
                if (result.Value)
                {
                    upload.Files.Clear();
                    foreach (System.IO.FileInfo f in file.Files)
                    {
                        WcfUploadFileService.FileList filelist = new WcfUploadFileService.FileList();
                        filelist.SavePath = "20120222";
                        filelist.BufferSize = 1024 * 32;
                        filelist.File = f;
                        upload.Files.Add(filelist);
                    }

                    upload.BeginUpload();
                }
            }
        }

    }



HOHO运行一下看效果怎么样吧!


项目源码 我接触siverlight和wcf不久,望大牛指教啊。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐