C#文件流读写和进度回调示例详解
作者:天兵公园 发布时间:2022-08-12 14:52:31
前言
前不久遇到一个问题,是公司早期的基础库遇到的,其实很低级,但是还是记录下来。出错点是一个 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 的倍数,永远会在文件尾部多补充上一段的冗余数据。
这里整整多出了 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 等等 ...
来源:https://www.jianshu.com/p/33ed9e665620


猜你喜欢
- MongoDB是介于关系数据库和非关系数据库之间的一种产品,文件的存储格式为BSON(一种JSON的扩展),这里就主要介绍Java通过使用m
- 一、定义登录控制器目录结构代码:1、创建TUser类package com.demo.pojo;import lombok.AllArgsC
- 首先添加一个timer,50susing System;using System.Collections.Generic;using Sys
- 1. 拷贝 Mybatis 反向工程配置文件到项目的根目录下2. 根据项目及表的情况,修改 GeneratorMapper.xml 配置如果
- 一、Hash加密,使用HashAlgorithm哈希算法类的派生类(MD5、SHA1等)特点:只能加密,不可逆。可对目标信息生成一段特定长度
- VC和BCB中做一个Server的监听程序,只需要指定端口,然后监听(Listen)就行了.在C#找不到这个函数了,慢慢看MSDN,怎么需要
- 前言之前用简书的时候一直是在web端,后来下载了客户端,看到了搜索的那个动画,就尝试的去写了,没写之前感觉挺容易的,写了之后,就感觉里面还是
- Java 向上转型和向下转型的详解转型是在继承的基础上而言的,继承是面向对象语言中,代码复用的一种机制,通过继承,子类可以复用父
- 首先看一看什么是装箱和拆箱?简单的来说:装箱就是值类型转换为引用类型;拆箱就是引用类型转换为值类型。值类型,包括原类型(Sbyte、Byte
- makeCertPic.javapackage pic;import java.awt.Color;import java.awt.Font
- 最近在做一个资源共享的项目中,采用了Struts2.1.8+Spr
- 就是每隔一定的时间显示一张图片,全部图片文件位于:“工作空间\项目名称\bin\动态图\花好月圆\”文件夹下。文件名类似:1001.jpg,
- 记得在2013年12月的时候,有系列文章是介绍怎么开发一个智能手表的App,让用户可以在足球比赛中记录停表时间。随着Android Wear
- Activity回顾activity是android程序中最重要的组件之一,它是用户与android用户交互的主要组件,类似于桌面程序的图形
- 1. 前言Spring的核心技术IOC(Intorol of Converse控制反转)的实现途径是DI(dependency Insert
- 本文实例讲述了Android获取当前已连接的wifi信号强度的方法,是Android程序开发中非常常见的重要技巧。分享给大家供大家参考之用。
- 标题index界面加载问题刚开始学习springBoot记录一下遇到的小问题1.index.html加载不出来的问题我习惯性的将index.
- 前言废话不多说直接开始老规矩,文章最后有源码完成效果图棋子加渐变色棋子不加渐变色一、测量1.获取宽高 @Override protected
- using System;using System.Collections;using System.Xml;namespace Jb51.
- * String类是不可变类,只要对String进行修改,都会导致新的对象生成。* StringBuffer和StringBuilder都是