文件的合并无非就是用流把所有的文件都写到同一个文件里。但有时候遇到大文件的时候,会出现内存溢出等情况,为了解决这个问题,我们可以考虑分段循环来读写文件。(目前测试的最大文件是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的话 ,在下一篇文件加解密最下面附有下载地址

posted on 2011-07-29 11:32  写代码的女孩  阅读(1411)  评论(1编辑  收藏  举报