前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# 温故而知新:Stream篇(四)下

C# 温故而知新:Stream篇(四)下

作者头像
逸鹏
发布2018-04-10 15:30:19
6820
发布2018-04-10 15:30:19
举报
文章被收录于专栏:逸鹏说道逸鹏说道

上面的例子是将一个文件作为整体进行操作,这样会带来一个问题,当文件很大或者网络不是很稳定的时候会发生意想不到的错误

那我们该怎么解决这一问题呢?其实有种思路还是不错的,那就是分段传输:

那就DIY一个简单的分段传输的例子,我们先将处理每一段的逻辑先整理好

代码语言:javascript
复制
/// <summary>
    /// 分段上传例子
    /// </summary>
    public class UpFileSingleTest
    {
        //我们定义Buffer为1000
        public const int BUFFER_COUNT = 1000;


        /// <summary>
        /// 将文件上传至服务器(本地),由于采取分段传输所以,
        /// 每段必须有一个起始位置和相对应该数据段的数据
        /// </summary>
        /// <param name="filePath">服务器上文件地址</param>
        /// <param name="startPositon">分段起始位置</param>
        /// <param name="btArray">每段的数据</param>
        private void WriteToServer(string filePath,int startPositon,byte[] btArray) 
        {
            FileStream fileStream = new FileStream(filePath, FileMode.OpenOrCreate);
            using (fileStream) 
            {
                //将流的位置设置在该段起始位置
                fileStream.Position = startPositon;
                //将该段数据通过FileStream写入文件中,每次写一段的数据,就好比是个水池,分段蓄水一样,直到蓄满为止
                fileStream.Write(btArray, 0, btArray.Length);
            }
        }


        /// <summary>
        /// 处理单独一段本地数据上传至服务器的逻辑,根据客户端传入的startPostion
        /// 和totalCount来处理相应段的数据上传至服务器(本地)
        /// </summary>
        /// <param name="localFilePath">本地需要上传的文件地址</param>
        /// <param name="uploadFilePath">服务器(本地)目标地址</param>
        /// <param name="startPostion">该段起始位置</param>
        /// <param name="totalCount">该段最大数据量</param>
        public void UpLoadFileFromLocal(string localFilePath,string uploadFilePath,int startPostion,int totalCount) 
        {
            //if(!File.Exists(localFilePath)){return;}
           //每次临时读取数据数
            int tempReadCount = 0;
            int tempBuffer = 0;
            //定义一个缓冲区数组
            byte[] bufferByteArray = new byte[BUFFER_COUNT];
            //定义一个FileStream对象
            FileStream fileStream = new FileStream(localFilePath,FileMode.Open);
            //将流的位置设置在每段数据的初始位置
            fileStream.Position = startPostion;
            using (fileStream)
            {
                //循环将该段数据读出在写入服务器中
                while (tempReadCount < totalCount)
                {
 
                    tempBuffer = BUFFER_COUNT;
                    //每段起始位置+每次循环读取数据的长度
                    var writeStartPosition = startPostion + tempReadCount;
                    //当缓冲区的数据加上临时读取数大于该段数据量时,
                    //则设置缓冲区的数据为totalCount-tempReadCount 这一段的数据
                    if (tempBuffer + tempReadCount > totalCount) 
                    {
                        //缓冲区的数据为totalCount-tempReadCount 
                        tempBuffer = totalCount-tempReadCount;
                        //读取该段数据放入bufferByteArray数组中
                        fileStream.Read(bufferByteArray, 0, tempBuffer);
                        if (tempBuffer > 0) 
                        {
                            byte[] newTempBtArray = new byte[tempBuffer];
                            Array.Copy(bufferByteArray, 0, newTempBtArray, 0, tempBuffer);
                            //将缓冲区的数据上传至服务器
                            this.WriteToServer(uploadFilePath, writeStartPosition, newTempBtArray);
                        }
 
                    }
                    //如果缓冲区的数据量小于该段数据量,并且tempBuffer=设定BUFFER_COUNT时,通过
                    //while 循环每次读取一样的buffer值的数据写入服务器中,直到将该段数据全部处理完毕
                    else if (tempBuffer == BUFFER_COUNT) 
                    {
                        fileStream.Read(bufferByteArray, 0, tempBuffer);
                        this.WriteToServer(uploadFilePath, writeStartPosition, bufferByteArray);
                    }
 
                    //通过每次的缓冲区数据,累计增加临时读取数
                    tempReadCount += tempBuffer;
                }
            }
        }
    }

一切准备就绪,我们剩下的就是将文件切成几段进行上传了

代码语言:javascript
复制
static void Main(string[] args)
        {
            UpFileSingleTest test=new UpFileSingleTest();
            FileInfo info = new FileInfo(@"G:\\Skyrim\20080204173728108.torrent");
            //取得文件总长度
            var fileLegth = info.Length;
            //假设将文件切成5段
            var divide = 5;
            //取到每个文件段的长度
            var perFileLengh = (int)fileLegth / divide;
            //表示最后剩下的文件段长度比perFileLengh小
            var restCount = (int)fileLegth % divide;
            //循环上传数据
            for (int i = 0; i < divide+1; i++)
            {
                //每次定义不同的数据段,假设数据长度是500,那么每段的开始位置都是i*perFileLength
                var startPosition = i * perFileLengh;
                //取得每次数据段的数据量
                var totalCount = fileLegth - perFileLengh * i > perFileLengh ? perFileLengh : (int)(fileLegth - perFileLengh * i);
                //上传该段数据
                test.UpLoadFileFromLocal(@"G:\\Skyrim\\20080204173728108.torrent", @"G:\\Skyrim\\20080204173728109.torrent", startPosition, i == divide ? divide : totalCount);
            }
        }

上传结果:

总的来说,分段传输比直接传输复杂许多,我会在今后的例子中加入多线程,这样的话每段数据的传输都能通过一个线程单独处理,能够提升上传性能和速度

本章总结

本章介绍了Stream中最关键的派生类FileStream的概念,属性,方法,构造函数等重要的概念,包括一些难点和重要点都一一列举出来,最后2个例子让大家在温故下

FileStream的使用方法,包括FileStream异步同步操作和分段传输操作。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 我为Net狂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档