文件的合并无非就是用流把所有的文件都写到同一个文件里。但有时候遇到大文件的时候,会出现内存溢出等情况,为了解决这个问题,我们可以考虑分段循环来读写文件。(目前测试的最大文件是4.8G)
下面我写说说合并文件的原理:首先是用一个文本文件来记录文件的目录结构(无论你文件夹有多少个层级目录,还原时有了它问题就简单了很多),最后把这个文件和要合并的文件一同写到大文件里面。
拆分的原理:先读出记录目录结构的文件,然后先循环创建文件夹。之后再一个一个还原文件。
接下来,我把代码贴出来供大家参考参考:
Common.SaveFilePath //表示的是记录层级目录的文件
/// <summary>
/// 初始化层级目录文件
/// </summary>
private void CreateInit()
{
//每次合并文件前保证这个文件是不存在的,以免目录结构混乱
if (File.Exists(Common.SaveFilePath))
{
File.Delete(Common.SaveFilePath);
}
if (!File.Exists(Common.SaveFilePath))
{
using (FileStream fs = File.Create(Common.SaveFilePath))
{
Byte[] info = new UTF8Encoding(true).GetBytes("");
fs.Write(info, 0, info.Length);
}
}
}
/// <summary>
/// 记录文件夹的层级关系
/// </summary>
/// <param name="path">要合并的文件夹或文件路径</param>
private void GetRecord(string path)
{
try
{
if (Directory.Exists(path))
{
string[] wang = Directory.GetFiles(path);
for (int j = 0; j < wang.ToList().Count; j++)
{
using (FileStream fs = File.Open(Common.SaveFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
//如果不执行一下,就总是一条数据,执行一下,就能顺序添加
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(b, 0, b.Length) > 0)
{
fs.Flush();
}
Byte[] info = new UTF8Encoding(true).GetBytes(wang[j] + '\r' + '\n');
fs.Write(info, 0, info.Length);
}
}
string[] kid = Directory.GetDirectories(path);//选择文件夹下的子文件夹
for (int i = 0; i < kid.ToList().Count; i++)
{
using (FileStream fs = File.Open(Common.SaveFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
//如果不执行一下,就总是一条数据,执行一下,就能顺序添加
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(b, 0, b.Length) > 0)
{
fs.Flush();
}
Byte[] info = new UTF8Encoding(true).GetBytes(kid[i] + '\r' + '\n');
fs.Write(info, 0, info.Length);
fs.Close();
GetRecord(kid[i]);
}
}
}
else
{
using (FileStream fs = File.Open(Common.SaveFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
Byte[] info = new UTF8Encoding(true).GetBytes(path + '\r' + '\n');
//如果不执行一下,就总是一条数据,执行一下,就能顺序添加
byte[] b = new byte[info.Length];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(b, 0, b.Length) > 0)
{
fs.Flush();
}
fs.Write(info, 0, info.Length);
}
}
}
catch (Exception)
{
MessageBox.Show(@"层级目录没有生成");
}
}
/// <summary>
/// 合并其中内容(包含文件)
/// </summary>
private void UnionFile()
{
int counter = 0;
string line;
string[] file_names;//里面存放的是所有文件的路径包括文件名
using (StreamReader file = new StreamReader(Common.SaveFilePath))
{
while ((line = file.ReadLine()) != null)
{
//判断是文件夹还是文件
FileInfo fileInfo = new FileInfo(line);
if ((fileInfo.Attributes & FileAttributes.Directory) == 0)//文件
{
counter++;
}
}
file_names = new string[counter + 1];//索引值必须比加密文件夹下的所有文件个数相加多一个才可以,因为要考虑到添加的那个层级目录文件
}
using (StreamReader file = new StreamReader(Common.SaveFilePath))
{
int okb = 0;
while ((line = file.ReadLine()) != null)
{
//判断是文件夹还是文件
string savefile = line.Substring(line.IndexOf("|") + 1);
FileInfo fileInfo = new FileInfo(savefile);
if ((fileInfo.Attributes & FileAttributes.Directory) == 0)
{
file_names.SetValue(savefile, ++okb);
}
}
file_names.SetValue(Common.SaveFilePath, 0);
//到此文件合并的准备工作完成,接下来开始文件的合并
Packer.Pack(Common.TmpFilePath, file_names);//文件打包
}
}
public class Header
{
public long Length;
public string Filename;
/// <summary>
/// 向合并之后的文件写入每个文件的名称和长度
/// </summary>
/// <param name="fs"></param>
public void WriteTo(Stream fs)
{
byte[] len = BitConverter.GetBytes(Length);
byte[] buf = new byte[24680];//如果文件名太长,必须扩大数组范围
byte[] str = Encoding.Unicode.GetBytes(Filename);
str.CopyTo(buf, 0);
fs.Write(len, 0, len.Length);
fs.Write(buf, 0, buf.Length);
}
/// <summary>
/// 从大文件中读取单文件的长度和名称
/// </summary>
/// <param name="fs"></param>
/// <returns></returns>
public bool ReadFrom(Stream fs)
{
byte[] len = BitConverter.GetBytes(Length);
byte[] buf = new byte[24680];//如果文件名太长,必须扩大数组范围
if (len.Length != fs.Read(len, 0, len.Length)) return false;
if (buf.Length != fs.Read(buf, 0, buf.Length)) return false;
Length = BitConverter.ToInt64(len, 0);
Filename = Encoding.Unicode.GetString(buf).Trim(new char[] { '\0' });
return true;
}
}
public class Packer
{
/// <summary>
/// 文件打包
/// </summary>
/// <param name="resultFilename">要打包的文件或文件夹</param>
/// <param name="filenames">所有文件的路径包括文件名</param>
public static void Pack(string resultFilename, params string[] filenames)
{
using (FileStream fout = new FileStream(resultFilename, FileMode.Create, FileAccess.Write))
{
for (int i = 0; i < filenames.Length; i++)
{
using (FileStream fin = new FileStream(filenames[i], FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
Header header = new Header();
header.Length = fin.Length;
//header.filename = Path.GetFileName(filenames[i]);
header.Filename = filenames[i];
header.WriteTo(fout);
byte[] buf;
if (header.Length > 1024 * 1024 * 32)
{
buf = new byte[1024 * 1024 * 32];
}
else
{
buf = new byte[header.Length];
}
BinaryReader br = new BinaryReader(fin);
BinaryWriter bw = new BinaryWriter(fout);
while (fin.Position < fin.Length)
{
int readCount = br.Read(buf, 0, buf.Length);
bw.Write(buf, 0, readCount);
}
}
}
}
}
/// <summary>
/// 拆分文件
/// </summary>
/// <param name="filename">要拆分的文件路径</param>
public static void Unpack(string filename)
{
using (FileStream fin = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
Header header = new Header();
int counter = 0;
string line;
while (header.ReadFrom(fin))
{
unpackedFiles.Add(header.Filename);
byte[] buf;
buf = new byte[1024 * 1024 * 32];
long num = header.Length / (1024 * 1024 * 32);
long slack = header.Length % (1024 * 1024 * 32);
BinaryReader br = new BinaryReader(fin);
using (FileStream fout = new FileStream(header.Filename, FileMode.Create, FileAccess.Write))
{
BinaryWriter bw = new BinaryWriter(fout);
for (int j = 0; j < num; j++)
{
int readCount = br.Read(buf, 0, buf.Length);
bw.Write(buf, 0, readCount);
}
if (slack > 0)
{
int readCount = br.Read(buf, 0, (int)slack);
bw.Write(buf, 0, readCount);
}
bw.Flush();
}
//解压出第一个文件时,该文件就是层级目录,要按照层级目录,创建文件夹,这样,那些文件才会有归属
if (counter == 0)
{
using (StreamReader file = new StreamReader(Common.SaveFilePath))
{
while ((line = file.ReadLine()) != null)
{
//判断是文件夹还是文件
FileInfo fileInfo = new FileInfo(line);
if ((fileInfo.Attributes & FileAttributes.Directory) != 0)
{
if (fileInfo.DirectoryName != null)
{
Directory.CreateDirectory(fileInfo.DirectoryName);
}
}
}
}
counter = 1;
}
}
}
}
}
接下来调用以上方法试试:
合并:
CreateInit();
GetRecord(path); //生成层级目录 path 表示您要合并的文件或文件夹路径
UnionFile();//合并文件
File.Delete(Common.SaveFilePath); //合并完后将记录层级目录的文件删除
拆分:
Packer.Unpack(path);//path 表示您要拆分的文件路径
想要完整的demo的话 ,在下一篇文件加解密最下面附有下载地址