在公司做项目的时候,有打包和解包文件的需求,而文件为任意类型(eg:txt、exe、dll、jpg、osg、bmp、avi...),刚遇到这个问题,满脑子都是这么多格式的文件仅用打包和解包两个接口如何实现呢,简直难死了,又不能不实现这个功能,就喝了两袋咖啡,冷静半个钟头,决定好好研究一番。

  先从文件这个概念入手,相信软件研发的人员都不陌生,计算机存储都是以文件的形式,对文件的操作分为三步:打开文件-读写-关闭文件(与把大象放在冰箱里是一个流程,哈哈),看起来简简单单的三步,却花费了我两周的时间。

  先说说打包吧,就是把所有的文件打包在一个.dat文件中(二进制的,至于什么名字嘛,你开心就好,无所谓啦),要打包的文件,实则分为两部分:文件和文件夹,所以打包的接口我就写的麻烦了些,把文件和文件夹分开:

bool PackFileAndDirectory(const std::vector<std::string>& files, const std::vector<std::string>& directories, const std::string& outputfilename);

参数说明: files: 文件(全路径)、directories: 文件夹(全路径)、outputfilename: 打包后输出的文件名(全路径)、返回值: 成功返回true,失败返回false  

  解包的接口设计,即:有一个打包好的文件,你把它解包成多个文件,并且文件的级别信息不能出错,内容也不能张冠李戴(类似于咱们用windows的一键解包)

bool UnPackFileAndDirectory(const std::string& inputzipfile, const std::string& outputpath);解包文件和文件夹到某一个目录下

参数说明:inputzipfile: 待解包的文件(全路径)、outputpath: 输出到的目标位置,将解包后的所有文件和文件夹放到该文件夹下(全路径)、返回值: 成功返回true,失败返回false

  先针对打包接口:

  第一个参数:文件,这个比较简单,就是把所有文件的全路径放在vector中,能利用全路径获取到文件的基本信息:文件名、文件内容等等。

  第二个参数:文件夹,这个就会麻烦些,因为存在多个文件夹,每个文件夹又可能是包含多级子目录,先拿一个文件夹来说吧,首先我们必须获取这个文件夹的信息:文件夹的名字、名字长度、路径等等,还要设计一个遍历文件夹所有子目录的函数,保留级别的对应关系,在路径这一块,我想了很久,不能用全路径(为啥呢->假如全路径,那等解包的时候,别人让你解包在哪个盘就哪个盘,你总不能带着自己原先所在盘的路径吧 (eg:把C:/test/file1.txt打包好了,解包需求是解在:D:/tmp/unpack/下,你发现自己的打包文件中存的路径信息是:C:/test/file1.txt,解包后就会出现D:/tmp/unpack/C:/test/file1.txt,这样怎么可能生成一个文件呢),所以我采用了一种相对路径法:解包路径+自己的相对路径,完美的解决了路径问题

  文件:统计文件个数、获取文件信息、获取文件内容; 文件夹:从保留多级目录关系的思路中跳出来,保留路径信息(不是原路径信息,而是解包后的全路径信息),文件夹内容为0,文件的内容进行获取,遍历所有子级+父级的文件个数、文件信息等等

  遍历一个文件夹的所有子目录:

 1 void GetAllSubFiles(string path, vector<string>& files)//获取所有的子文件
 2 {
 3     long hFile = 0;
 4     struct _finddata_t fileinfo;
 5     string p;
 6     if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
 7     {
 8         do
 9         {
10             if ((fileinfo.attrib &  _A_SUBDIR))
11             {
12                 if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
13                 {
14                     files.push_back(p.assign(path).append("\\").append(fileinfo.name));
15                     GetAllSubFiles(p.assign(path).append("\\").append(fileinfo.name), files);
16                 }
17             }
18             else
19             {
20                 files.push_back(p.assign(path).append("\\").append(fileinfo.name));
21             }
22 
23         } while (_findnext(hFile, &fileinfo) == 0);
24 
25         _findclose(hFile);
26     }
27 }

 

  打包文件部分:

 1 bool PackFileAndDirectory(const vector<string>& files, const vector<string>& directories, const string& outputfilename) //打包:写文件的过程
 2 {
 3     FILE *wfp = NULL;  
 4     wfp = fopen(outputfilename.c_str(), "wb");
 5     if (wfp == NULL)
 6     {
 7         cout << "打包:文件打开失败!" << endl;
 8     }
 9 
10     /************************第一个参数:文件vector**********************/
11 
12     int fileCount = files.size();
13     fwrite(&fileCount, sizeof(fileCount), 1, wfp);
14 
15     for (size_t i = 0; i < fileCount; i++)
16     {
17         struct FileInfo file;
18 
19         string path = files[i]; //双斜杠路径名
20         string filename = getFileNameFromPath(path);//根据路径获取文件名
21         strcpy(file.FileName, filename.c_str());
22         file.fileNameLen = strlen(filename.c_str());
23 
24         ifstream fin;
25         fin.open(path, ios::binary);
26 
27         fin.seekg(0, ios::end);
28         streampos sp = fin.tellg();
29         file.fileSize = sp; //文件的size
30 
31         fwrite(&file, sizeof(file), 1, wfp);
32         cout << "filename:" << file.FileName << ";  filenameLength:" << file.fileNameLen << ";  fileSize:" << file.fileSize << endl;
33 
34         FILE *files = fopen(path.c_str(), "rb");
35         eachFile = new unsigned char[file.fileSize];
36         fileBuffer.push_back(eachFile);
37         fread(fileBuffer[i], file.fileSize, 1, files);
38 
39         fwrite(fileBuffer[i], file.fileSize, 1, wfp);
40     }
41 }

 

  今天先说这么多,打包里面有很多细小的知识点,真正去做才会发现,所以必须巩固自己的基础,作为一名职场小白+程序媛,文中假如有需要改进的观点和看法,还望各位IT界的大神赐教,O(∩_∩)O~

 

posted on 2018-08-16 10:16  才疏学浅、  阅读(1862)  评论(2编辑  收藏  举报