C# 温故而知新:Stream篇(四)下
时间:2022-05-07
本文章向大家介绍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:\Skyrim20080204173728108.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异步同步操作和分段传输操作。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 在前端如何玩转 Word 文档
- DataGrid删除确认及Item颜色交替
- 详解Apache下.htaccess文件常用配置
- 你的模型需要解释(二)
- 算法工程师-SQL进阶:强大的Case表达式
- SOLID设计原则和我的一点个人感悟
- SpringBoot整合Freemarker使用
- Vm常见虚拟网络模式
- 设计模式 | 桥接模式
- Supervisor快速入门 | 使用Supervisor守护Nginx进程
- 技术选型的艺术---湖北技术价值分享会
- SpringBoot 配置文件编写及使用方式 (拒绝硬编码)
- Docker六脉神剑 (六) 1. Docker集群之Kubernetes(K8S) 了解k8s - 理论篇
- SpringBoot thymeleaf自定义错误页面
- SpringBoot 配置Redis操作