C++ 使用UNAR库-将文件提取到filestream缓冲区
我需要的是能够将.rar文件中的文件提取到流中。我正在创建一个测试用例来了解如何使用。我已经搜索和修补了一段时间,但我不知道如何使用这个库。考虑到.rar归档文件的普遍性,我很惊讶我甚至找不到它的文档或教程 我自己也取得了一些进步,但并不总是奏效。某些文件被正确提取。其他文件由于某种原因而变得混乱(但并非完全是“垃圾”二进制数据)。到目前为止,我所知道的通常是(但并非总是):C++ 使用UNAR库-将文件提取到filestream缓冲区,c++,compression,unrar,C++,Compression,Unrar,我需要的是能够将.rar文件中的文件提取到流中。我正在创建一个测试用例来了解如何使用。我已经搜索和修补了一段时间,但我不知道如何使用这个库。考虑到.rar归档文件的普遍性,我很惊讶我甚至找不到它的文档或教程 我自己也取得了一些进步,但并不总是奏效。某些文件被正确提取。其他文件由于某种原因而变得混乱(但并非完全是“垃圾”二进制数据)。到目前为止,我所知道的通常是(但并非总是): 非工作文件具有fileInfo.Method=48。它们似乎是压缩比为100%的文件,即没有压缩 工作文件有fileI
- 非工作文件具有
。它们似乎是压缩比为100%的文件,即没有压缩fileInfo.Method=48
- 工作文件有
、fileInfo.Method=49
、50
、51
或52
,它们对应于压缩速度,最快、最快、正常、良好、最佳53
/* put in the same directory as the unrar source files
* compiling with:
* make clean
* make lib
* g++ rartest.cpp -o rartest libunrar.so -lboost_filesystem
*/
#include <cstring>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#define _UNIX
#define RARDLL
#include "dll.hpp"
using namespace std;
namespace fs = boost::filesystem;
//char fileName[100] = "testout0.jpg\0";
//
//// doens't work
//int PASCAL ProcessDataProc(unsigned char* buffer, int buffLen) {
// cout << "writing..." << endl;
// ofstream outFile(fileName);
// cout << buffLen << endl;
// cout << outFile.write((const char*)buffer, buffLen) << endl;
// cout << "done writing..." << endl;
// fileName[7]++;
//}
int CALLBACK CallbackProc(unsigned int msg, long myBuffer, long rarBuffer, long bufferLen) {
switch(msg) {
case UCM_CHANGEVOLUME:
break;
case UCM_PROCESSDATA:
memcpy((char*)myBuffer, (char*)rarBuffer, bufferLen);
break;
case UCM_NEEDPASSWORD:
break;
}
return 1;
}
int main(int argc, char* argv[]) {
if (argc != 2)
return 0;
ifstream archiveStream(argv[1]);
if (!archiveStream.is_open())
cout << "fstream couldn't open file\n";
// declare and set parameters
HANDLE rarFile;
RARHeaderDataEx fileInfo;
RAROpenArchiveDataEx archiveInfo;
memset(&archiveInfo, 0, sizeof(archiveInfo));
archiveInfo.CmtBuf = NULL;
//archiveInfo.OpenMode = RAR_OM_LIST;
archiveInfo.OpenMode = RAR_OM_EXTRACT;
archiveInfo.ArcName = argv[1];
// Open file
rarFile = RAROpenArchiveEx(&archiveInfo);
if (archiveInfo.OpenResult != 0) {
RARCloseArchive(rarFile);
cout << "unrar couldn't open" << endl;
exit(1);
}
fileInfo.CmtBuf = NULL;
cout << archiveInfo.Flags << endl;
// loop through archive
int numFiles = 0;
int fileSize;
int RHCode;
int PFCode;
while(true) {
RHCode = RARReadHeaderEx(rarFile, &fileInfo);
if (RHCode != 0) break;
numFiles++;
fs::path path(fileInfo.FileName);
fileSize = fileInfo.UnpSize;
cout << fileInfo.Method << " " << fileInfo.FileName << " (" << fileInfo.UnpSize << ")" << endl;
char fileBuffer[fileInfo.UnpSize];
// not sure what this does
//RARSetProcessDataProc(rarFile, ProcessDataProc);
// works for some files, but not for others
RARSetCallback(rarFile, CallbackProc, (long) &fileBuffer);
PFCode = RARProcessFile(rarFile, RAR_TEST, NULL, NULL);
// properly extracts to a directory... but I need a stream
// and I don't want to write to disk, read it, and delete from disk
//PFCode = RARProcessFile(rarFile, RAR_EXTRACT, ".", fileInfo.FileName);
// just skips
//PFCode = RARProcessFile(rarFile, RAR_SKIP, NULL, NULL);
if (PFCode != 0) {
RARCloseArchive(rarFile);
cout << "error processing this file\n" << endl;
exit(1);
}
ofstream outFile(path.filename().c_str());
outFile.write(fileBuffer, fileSize);
}
if (RHCode != ERAR_END_ARCHIVE)
cout << "error traversing through archive: " << RHCode << endl;
RARCloseArchive(rarFile);
cout << "num files: " << numFiles << endl;
}
/*与UNRR源文件放在同一目录中
*使用以下工具进行编译:
*澄清
*使自由
*g++rartest.cpp-o rartest libunrar.so-lboost_文件系统
*/
#包括
#包括
#包括
#包括
#定义UNIX
#定义RARDLL
#包括“dll.hpp”
使用名称空间std;
名称空间fs=boost::filesystem;
//char fileName[100]=“testout0.jpg\0”;
//
////不起作用
//int PASCAL ProcessDataProc(无符号字符*缓冲区,int buffLen){
//cout您似乎发布了一些源代码,但没有实际问题
您是否考虑过查看(哪些指向他们的
另见:
我也无法在网上找到任何文档,但您可以使用以下示例:
转到,并在页面左下角输入关键字,如RAROpenArchiveEx
。您将看到使用UNRR库的各种开源项目的头文件和源文件
这应该可以让你开始了。我不熟悉UNRR,在快速阅读文档后,我认为你假设每个文件只调用CallbackProc一次。但是,我认为UNRR可能会多次调用它。它先解压缩一些数据,然后调用CallbackProc
,然后解压缩下一个数据块,然后再次调用CallbackProc
,该过程将迭代,直到处理完所有数据。
您应该记住实际写入缓冲区的字节数,并在相应的偏移量处追加新数据。我无法获取(即使是空的main()
)编译。我正在尝试将rar存档中的文件解压缩到缓冲区,如我所愿。我查看了您的链接。据我所知,RARLAB不支持他们的UNRR库-只是一个开源UNRR库,没有文档。可能EOL字符有问题(存档在Windows上制作,但在Unix上提取),但我不太确定..我确保在读/写缓冲区时使用正确的buffLen
或fileSize
。现在,我准备把责任推到unrar库上。谢谢!我会看一看其中的一些。希望至少有一个摘录直接放到缓冲区,我可以找到oUT如何做同样的事情。它明确解释了为什么失败的提取文件被混淆,但不是所有的垃圾数据。我重读文档,它没有给我任何印象,UNRAR可以执行每次文件多次的回调。你怎么想?有一个回调在提取过程中周期性地执行。对我来说似乎非常直观。我想原因是存档中的文件可能足够大,无法放入可用内存。将这些文件完全解压缩到一个缓冲区是不可能的,或者至少效率低下。
/* put in the same directory as the unrar source files
* compiling with:
* make clean
* make lib
* g++ rartest.cpp -o rartest libunrar.so -lboost_filesystem
*/
#include <cstring>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/crc.hpp>
#define _UNIX
#define RARDLL
#include "dll.hpp"
using namespace std;
namespace fs = boost::filesystem;
//char fileName[100] = "testout0.jpg\0";
//
//// doens't work
//int PASCAL ProcessDataProc(unsigned char* buffer, int buffLen) {
// cout << "writing..." << endl;
// ofstream outFile(fileName);
// cout << buffLen << endl;
// cout << outFile.write((const char*)buffer, buffLen) << endl;
// cout << "done writing..." << endl;
// fileName[7]++;
//}
int CALLBACK CallbackProc(unsigned int msg, long myBufferPtr, long rarBuffer, long bytesProcessed) {
switch(msg) {
case UCM_CHANGEVOLUME:
return -1;
break;
case UCM_PROCESSDATA:
memcpy(*(char**)myBufferPtr, (char*)rarBuffer, bytesProcessed);
*(char**)myBufferPtr += bytesProcessed;
return 1;
break;
case UCM_NEEDPASSWORD:
return -1;
break;
}
}
int main(int argc, char* argv[]) {
if (argc != 2)
return 0;
ifstream archiveStream(argv[1]);
if (!archiveStream.is_open())
cout << "fstream couldn't open file\n";
// declare and set parameters
RARHANDLE rarFile; // I renamed this macro in dll.hpp for my own purposes
RARHANDLE rarFile2;
RARHeaderDataEx fileInfo;
RAROpenArchiveDataEx archiveInfo;
memset(&archiveInfo, 0, sizeof(archiveInfo));
archiveInfo.CmtBuf = NULL;
//archiveInfo.OpenMode = RAR_OM_LIST;
archiveInfo.OpenMode = RAR_OM_EXTRACT;
archiveInfo.ArcName = argv[1];
// Open file
rarFile = RAROpenArchiveEx(&archiveInfo);
rarFile2 = RAROpenArchiveEx(&archiveInfo);
if (archiveInfo.OpenResult != 0) {
RARCloseArchive(rarFile);
cout << "unrar couldn't open" << endl;
exit(1);
}
fileInfo.CmtBuf = NULL;
// cout << archiveInfo.Flags << endl;
// loop through archive
int numFiles = 0;
int fileSize;
int RHCode;
int PFCode;
int crcVal;
bool workaroundUsed = false;
char currDir[2] = ".";
char tmpFile[11] = "buffer.tmp";
while(true) {
RHCode = RARReadHeaderEx(rarFile, &fileInfo);
if (RHCode != 0) break;
RARReadHeaderEx(rarFile2, &fileInfo);
numFiles++;
fs::path path(fileInfo.FileName);
fileSize = fileInfo.UnpSize;
crcVal = fileInfo.FileCRC;
cout << dec << fileInfo.Method << " " << fileInfo.FileName << " (" << fileInfo.UnpSize << ")" << endl;
cout << " " << hex << uppercase << crcVal << endl;
char fileBuffer[fileSize];
char* bufferPtr = fileBuffer;
// not sure what this does
//RARSetProcessDataProc(rarFile, ProcessDataProc);
// works for some files, but not for others
RARSetCallback(rarFile, CallbackProc, (long) &bufferPtr);
PFCode = RARProcessFile(rarFile, RAR_TEST, NULL, NULL);
// properly extracts to a directory... but I need a stream
// and I don't want to write to disk, read it, and delete from disk
// PFCode = RARProcessFile(rarFile, RAR_EXTRACT, currDir, fileInfo.FileName);
// just skips
//PFCode = RARProcessFile(rarFile, RAR_SKIP, NULL, NULL);
if (PFCode != 0) {
RARCloseArchive(rarFile);
cout << "error processing this file\n" << endl;
exit(1);
}
// crc check
boost::crc_32_type crc32result;
crc32result.process_bytes(&fileBuffer, fileSize);
cout << " " << hex << uppercase << crc32result.checksum() << endl;
// old workaround - crc check always succeeds now!
if (crcVal == crc32result.checksum()) {
RARProcessFile(rarFile2, RAR_SKIP, NULL, NULL);
}
else {
workaroundUsed = true;
RARProcessFile(rarFile2, RAR_EXTRACT, currDir, tmpFile);
ifstream inFile(tmpFile);
inFile.read(fileBuffer, fileSize);
}
ofstream outFile(path.filename().c_str());
outFile.write(fileBuffer, fileSize);
}
if (workaroundUsed) remove(tmpFile);
if (RHCode != ERAR_END_ARCHIVE)
cout << "error traversing through archive: " << RHCode << endl;
RARCloseArchive(rarFile);
cout << dec << "num files: " << numFiles << endl;
}