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

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

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

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

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

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

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异步同步操作和分段传输操作。

原文发布于微信公众号 - 我为Net狂(dotNetCrazy)

原文发表时间:2016-06-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java架构沉思录

分布式ID常见解决方案

在分布式系统中,往往需要对大量的数据如订单、账户进行标识,以一个有意义的有序的序列号来作为全局唯一的ID。

69420
来自专栏跟着阿笨一起玩NET

C#设计模式学习笔记-单例模式

  最近在学设计模式,学到创建型模式的时候,碰到单例模式(或叫单件模式),现在整理一下笔记。

10120
来自专栏Java面试通关手册

深入理解单例模式

Java面试通关手册(Java学习指南,欢迎Star,会一直完善下去,欢迎建议和指导):https://github.com/Snailclimb/Java_G...

20160
来自专栏一个会写诗的程序员的博客

并发编程之CAS(Compare and Swap)原理Unsafe类

AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent包中的基础类都是使用这种模式来实现...

14610
来自专栏编程

防止黑客SQL注入的方法

一、SQL注入简介 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚...

27570
来自专栏Hongten

python开发_shelve_完整版_博主推荐

=====================================================

9120
来自专栏JavaEE

mybatis-plus的使用 ------ 入门

mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQ...

4.6K40
来自专栏MasiMaro 的技术博文

OLEDB存取BLOB型数据

现代数据库系统除了支持一些标准的通用数据类型以外,大多数还支持一种称之为BLOB型的数据。 BLOB全称为big large object bytes, 大二...

16430
来自专栏GreenLeaves

存储过程详解

存储过程简介 什么是存储过程:存储过程可以说是一个记录集吧,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表...

235100
来自专栏Seebug漏洞平台

从WordPress SQLi谈PHP格式化字符串问题

0x00 背 景 近日,WordPress爆出了一个SQLi漏洞,漏洞发生在WP的后台上传图片的位置,通过修改图片在数据库中的参数,以及利用php的 sp...

37280

扫码关注云+社区

领取腾讯云代金券