C# 文件流读写以及进度回调

待兔 等级 993 0 0
标签: C#

前不久遇到一个问题,是公司早期的基础库遇到的,其实很低级,但是还是记录下来。出错点是一个 IO 流的写入bug,我们项目会有一种专有的数据格式,这个格式的奇葩点在于如果设置 IO 读缓冲区为 2014 字节的时候,整个文件刚好能读完,也就是说其 length 刚好是 1024 的倍数。后来在一次升级中增加了更多的文件格式,并且新的文件格式使用了新的自定义写入流,具有加密和压缩的作用,这样一来,文件的长度就不一定是 1024 的倍数了。

后来通过查看这个基础类的源代码发现因为是 .NET 2.0 时代的东西,也没有 Stream.Copy 的方法,于是当时的程序员手动写了个 Stream.Copy 的方法,我稍作改动为了更直观将输出流改为输出到文件,代码大概如下:

var fs_in = System.IO.File.OpenRead(@"C:\3.0.6.apk");
var fs_out = System.IO.File.OpenWrite(@"C:\3.0.6.apk.copy");
byte[] buffer = new byte[1024];
while (fs_in.Read(buffer,0,buffer.Length)>0)
{
    fs_out.Write(buffer, 0, buffer.Length);
}
Console.WriteLine("复制完成"); 

所以一眼就能看出这个方法简直有天大的 bug ,假设文件长度不为 1024 的倍数,永远会在文件尾部多补充上一段的冗余数据。

diff.PNG

这里整整多出了 878 字节的数据,导致整个文件都不对了,明显是基础知识都没学好。

增加一个变量保存实际读取到的字节数,改为如下:

var fs_in = System.IO.File.OpenRead(@"C:\迅雷下载\3.0.6.apk");
var fs_out = System.IO.File.OpenWrite(@"C:\迅雷下载\3.0.6.apk.copy");
byte[] buffer = new byte[1024];
int readBytes = 0;
while ((readBytes= fs_in.Read(buffer, 0, buffer.Length)) >0)
{
    fs_out.Write(buffer, 0, readBytes);
}
Console.WriteLine("复制完成"); 

对于处理大型文件,一般都有进度指示,比如处理压缩了百分多少之类的,这里我们也可以加上,比如专门写一个方法用于文件读取并返回 byte[] 和百分比。

static void ReadFile(string filename,int bufferLength, Action<byte[],int> callback)
{
    if (!System.IO.File.Exists(filename)) return;
    if (callback == null) return;
    System.IO.FileInfo finfo = new System.IO.FileInfo(filename);
    long fileLength = finfo.Length;
    long totalReadBytes = 0;
    var fs_in = System.IO.File.OpenRead(filename);
    byte[] buffer = new byte[bufferLength];
    int readBytes = 0;
    while ((readBytes = fs_in.Read(buffer, 0, buffer.Length)) > 0)
    {
        byte[] data = new byte[readBytes];
        Array.Copy(buffer, data, readBytes);
        totalReadBytes += readBytes;
        int percent = (int)((totalReadBytes / (double)fileLength) * 100);
        callback(data, percent);
    }
} 

调用就很简单了:

static void Main(string[] args)
{
    string filename = @"C:\3.0.6.apk";
    var fs_in = System.IO.File.OpenRead(filename);
    long ttc = 0;
    ReadFile(filename, 1024, (byte[] data, int percent) => 
    {
        ttc += data.Length;
        Console.SetCursorPosition(0, 0);
        Console.Write(percent+"%");
    });
    Console.WriteLine("len:"+ttc);
    Console.ReadKey();
} 

这是基于文件读取的,稍微改一下就可以改成输入流输出流的,这里就不贴出来了。文件读写非常耗时,特别是大文件,IO 和 网络请求都是 “重操作”,所以建议大家 IO 都放在单独的线程去执行。C# 中可以使用 Task、Thread、如果同时有多个线程需要执行就用 ThreadPool 或 Task,Java 或 Android 中用 Thread 或线程池,以及非常流行的 RxJava 等等 ...

收藏
评论区

相关推荐

C# 文件流读写以及进度回调
前不久遇到一个问题,是公司早期的基础库遇到的,其实很低级,但是还是记录下来。出错点是一个 IO 流的写入bug,我们项目会有一种专有的数据格式,这个格式的奇葩点在于如果设置 IO 读缓冲区为 2014 字节的时候,整个文件刚好能读完,也就是说其 length 刚好是 1024 的倍数。后来在一次升级中增加了更多的文件格式,并且新的文件格式使用了新的自定义写入
浅谈promise和js执行机制(一)
作为一个入门级前端,今天是一个非常值得纪念的日子,因为这是我第一次在论坛上发表帖子,作为起步。虽然我觉得自己水平还是十分的有限,对一些细节的理解还不是很透彻,但是还是要迈出这一步,不管是给别的新手作为学习参考,还是自己以后回顾,总觉得需要把自己的成长记录下来,希望自己以后还是要多坚持,如果有不对的地方还是希望大家及时提出来,共同进步 今天有时间翻到了
天池比赛数据挖掘心电图模型调参
Task4 建模与调参 4.1 学习目标 学习机器学习模型的建模过程与调参流程 完成相应学习打卡任务 4.2 内容介绍 逻辑回归模型: 理解逻辑回归模型; 逻辑回归模型的应用; 逻辑回归的优缺点; 树模型: 理解树模型; 树模型的应用; 树模型的优缺点; 集成模型 基于bagging思想的集成
Promise从入门到拿Offer之手写Promise
1、Promise构造函数的实现Promise构造函数用来声明示例对象,需要传入一个执行器函数。其中包括resolve函数和reject函数,以及几个重要的属性:状态属性、结果属性和回调函数队列。构造函数的基本框架 resolve函数用于异步处理成功后调用的函数。其中包括验证对象状态修改次数,修改promise实例对象状态,异步调用成功的回调函数
Vue 的 三种 watcher
userwatcher在页面中使用的watcher,即用户定义的watcher,用于观察一个属性的更新,支持数组定义多个,对象定义单个的形式,在initWatcher中进行watcher的初始化之后,在渲染函数进行数据的读取,触发依赖收集时会将userwatcher的依赖收集进去,data属性set更新时会被触发userwatcher所定义的回调函数(将新旧
4种主流超参数调优技术
> 点击上方“**机器学习与生成对抗网络**”,关注"星标" > > 获取有趣、好玩的前沿干货! ![](https://oscimg.oschina.net/oscnet/88766b7e-21b9-447b-
JSON API免费接口
#### JSON API免费接口 各种提供JSON格式数据返回服务网站的API接口 这里为大家搜集了一些能够返回JSON格式的服务接口。部分需要用JSONP调用。 ### 电商接口 * 淘宝商品搜索建议: http://suggest.taobao.com/sug?code=utf-8&q=商品关键字&callback
Java面试题总结
1)用过哪些设计模式,手写一个(除单例);  工厂模式,门面模式(个人有点像service层),代理模式 2)springmvc的核心是什么,请求的流程是怎么处理的,控制反转怎么实现的;  1 ,用户发送请求至前端控制器DispatcherServlet  2、DispatcherServlet收到请求调用HandlerMapping处理器映射器
12306 抢票系列之只要搞定RAIL_DEVICEID的来源,从此抢票不再掉线(中)
直奔重点 ---- ### 高楼大厦寻关键线索 Js文件中关于网络请求最典型的就是异步回调,将原本简单的操作复杂化,非要你等我,我等他,他还等着他的她. 最终直接结果就是整个请求流程反过来了,假设正常流程:是 A->B->C-D-E-F,那么异步请求很可能陷入这样的陷阱: F <- E <- D <- C <- B <- A 所以一层又一层的回调函数
2019年Android岗位BAT等大厂面试题,希望对新的一年的你有所帮助
2019年Android岗位BAT等大厂面试题知识点小结 ============================ 2019年了搜集了很多面试题,希望能对大家有所帮助 **1.View的绘制流程;自定义View如何考虑机型适配;自定义View的事件分发机制;View和ViewGroup分别有哪些事件分发相关的回调方法;自定义View如何提供获取View属
12306 抢票系列之只要搞定RAIL_DEVICEID的来源,从此抢票不再掉线(中)
直奔重点 ---- ### 高楼大厦寻关键线索 Js文件中关于网络请求最典型的就是异步回调,将原本简单的操作复杂化,非要你等我,我等他,他还等着他的她. 最终直接结果就是整个请求流程反过来了,假设正常流程:是 A->B->C-D-E-F,那么异步请求很可能陷入这样的陷阱: F <- E <- D <- C <- B <- A 所以一层又一层的回调函数
2019年Android岗位BAT等大厂面试题,希望对新的一年的你有所帮助
2019年Android岗位BAT等大厂面试题知识点小结 ============================ 2019年了搜集了很多面试题,希望能对大家有所帮助 **1.View的绘制流程;自定义View如何考虑机型适配;自定义View的事件分发机制;View和ViewGroup分别有哪些事件分发相关的回调方法;自定义View如何提供获取View属
Leetcode 703. 数据流中的第K大元素
### 1.题目要求 设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。 你的 **`KthLargest` 类**需要一个同时接收整数 `k` 和整数数组`nums` 的构造器,它包含数据流中的初始元素。每次调用 **`KthLargest.add`**,返回当前数据流中第K大的元素。 **示例:**
Promise 多重链式调用
`Promise`对象是用于异步操作的。 `Promise`的真正强大之处在于它的多重链式调用,可以避免层层嵌套回调。如果我们在第一次ajax请求后,还要用它返回的结果再次请求呢? 使用`Promise`,我们就可以利用`then`进行「链式回调」,将异步操作以同步操作的流程表示出来。 以下是个小Demo: /* e.g */ sen
Spring Security 实战干货:OAuth2登录获取Token的核心逻辑
![](https://oscimg.oschina.net/oscnet/22daf3d0-7e50-4b8d-b03a-dd4e0bbd55d8.jpg) 作者 | 码农小胖哥 来源 | https://mp.weixin.qq.com/s/zdTBdSVunqwVGx-spHjLjw ### 1\. 前言 在上一篇Spring Securi