使用 unrar 库 - 将文件提取到文件流缓冲区中

2024-05-25

我需要的是能够将 .rar 文件中的文件提取到流中。我正在创建一个测试用例来了解如何使用解压源文件 http://www.rarlab.com/rar/unrarsrc-3.9.9.tar.gz。我已经搜索和修补了一段时间,但我不知道如何使用该库。考虑到 .rar 档案的常见程度,我很惊讶我什至找不到它的文档或教程。

我自己取得了一些进步,但并不总是有效。某些文件已正确提取。其他文件由于某种原因而混乱(但不是完全地“垃圾”二进制数据)。到目前为止我所知道的是,通常(但并非总是):

  • 不工作的文件有fileInfo.Method = 48。它们似乎是压缩率为 100% 的文件 - 即没有压缩

  • 工作文件有fileInfo.Method = 49, 50, 51, 52, or 53,分别对应压缩速度:最快、快速、正常、良好、最佳

但我不知道为什么会这样。仍然找不到文档或工作示例。

以下是我迄今为止拥有的测试用例源和示例 rar 存档 http://www.mediafire.com/?yyimyi0mm52当用这个程序提取时,它既有工作文件也有非工作文件。

/* 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;

}

update:

我发现一个文件似乎(声称是?),但根据文件,我没有做错任何事情。我想我可能会被迫诉诸 CRC 检查缓冲区并在失败时实施解决方法。

解决方案来源(感谢 Denis Krjuchkov!):

/* 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;

}

我不熟悉 unrar,在快速阅读文档后,我认为您假设每个文件只调用 CallbackProc 一次。不过,我认为 unrar 可能会多次调用它。它解压一些数据然后调用CallbackProc,然后解压下一个数据块并再次调用CallbackProc,迭代该过程,直到处理完所有数据。 您应该记住实际写入缓冲区的字节数,并在相应的偏移量处追加新数据。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 unrar 库 - 将文件提取到文件流缓冲区中 的相关文章

随机推荐

  • RegEx 使用 match() 在 JavaScript 中提取字符串数组

    我正在尝试使用string match 在 javascript 中使用正则表达式来提取字符串数组 这是一个示例字符串 CREATE TABLE listings listing id INTEGER UNIQUE state TEXT t
  • 为什么函数会修改列表以及如何防止它发生?

    我正在 Python 3 7 x 中调用一个函数并向其传递一个列表 我愿意not希望修改列表 在函数内部 我复制了列表并对其进行修改 函数完成后 传递给函数的原始列表已被修改 为什么会发生这种情况 我该如何预防 这是代码 def appen
  • 如何复制身份列中的数据?

    我有一张桌子identity列在一台服务器中 并且在另一台服务器中有一个具有相同结构的其他表 现在我想将所有数据从一个表复制到另一个表 但我无能为力 我已经创建了一个链接服务器 我用这个 insert into server databas
  • 网页优化:为什么组合文件速度更快?

    我读过 将所有 css 文件合并为一个大文件 或将所有脚本文件合并为一个脚本文件 可以减少 HTTP 请求的数量 从而加快下载速度 但我不明白这一点 我认为如果你有多个文件 最多有一个限制 我相信在现代浏览器上是 10 个 浏览器会并行下载
  • 调用API“找不到模块”时AWS lambda层错误

    我尝试使用 AWS Lambda 层 观看了有关它的教程 但收到错误 找不到模块 service aws nodejs package exclude gitignore package json git provider name aws
  • WebRTC、getDisplayMedia() 不捕获远程流中的声音

    我有一个自己的网络应用程序 它基于peerjs库 它是一个视频会议 我正在尝试使用 MediaRecorder 进行录制 但我遇到了一个非常不愉快的情况 捕获我的桌面流的代码如下 let chooseScreen document quer
  • 如何在Python中仅列出顶级目录?

    我希望能够仅列出某个文件夹内的目录 这意味着我不需要列出文件名 也不需要其他子文件夹 让我们看看一个例子是否有帮助 在当前目录中我们有 gt gt gt os listdir os getcwd cx Oracle doc DLLs Doc
  • 合法管理员如何获取 Active Directory 中的用户密码?

    如果密码以可逆加密方式存储在 Active Directory 中 管理员 开发人员如何提取和解密该密码 具体来说 我指的是this http technet microsoft com en us library cc784581 WS
  • 将带有 itext 滚动条的表格的可编辑单元格设为只读

    请找到下面的代码 public class MakingFieldReadOnly implements PdfPCellEvent The resulting PDF public static final String RESULT1
  • mod_rewrite 可以转换任意数量、任意名称的参数吗?

    我对 mod rewrite 完全是个新手 我想做的事情听起来很简单 我不想拥有domain com script php a 1 b 2 c 3 我想要 domain com script a 1 b 2 c 3 问题是我的脚本采用各种组
  • 将数字 n 拆分为 k 个不同数字的总和

    我有一个数字 n 我必须将它分成 k 个数字 使得所有 k 个数字都是不同的 k 个数字的总和等于 n 并且 k 最大 例如 如果 n 为 9 则答案应为 1 2 6 如果 n 为 15 则答案应为 1 2 3 4 5 这就是我尝试过的 v
  • 如何在 Slack 机器人中获取用户名/用户 ID

    https github com DeronLee starbot git https github com DeronLee starbot git 我创建了一个 Slack 机器人 它运行良好 但是当有人向机器人发送消息时 我无法知道是
  • Bool类型返回规则

    我使用 dapper ORM 所以我使用两个规则Query
  • 使用webrtc时可以关闭SRTP吗

    现在我测试webrtc与SIP客户端 sx20 的通信 我使用 webrtc sdp 发送邀请消息 但 sip 客户端答案没有指纹 并且 sip 客户端的答案不是 SRTP 只是 RTP 所以我需要关闭WEBRTC中的SRTP 我可以做吗
  • 如果未设置,则从控制台读取 Makefile 变量

    我正在更新一个从外部源访问某些资源的 Makefile 即存在以下形式的规则 External cvs up 对于不受限制的资源 它可以按预期工作 现在 出现了功能漂移 外部资源需要更复杂的登录 因此规则已更改为与此没有太大不同的内容 Ex
  • 在Excel中过滤后打印可见区域的宏

    我有一个根据过滤表的宏column A价值观 现在我想打印only过滤器后的可见行 但遗憾的是它打印了所有行 包括过滤期间隐藏的顶部和底部行 在我的工作表中 有来自的数据Column A I 但打印区域只能是Columns C I 过滤后的
  • 詹金斯管道如果其他不工作

    我正在创建一个示例詹金斯管道 这是代码 pipeline agent any stages stage test steps sh echo hello stage test1 steps sh echo TEST stage test3
  • 访问结构向量

    我有一个结构 struct OutputStore int myINT string mySTRING 如果我创建一个 OutputStore 类型的数组 如下所示 OutputStore OutputFileData new Output
  • 如何在 .NET 中自定义 JSON 枚举的反序列化?

    我有以下示例 C 代码 它是使用 svcutil exe 应用程序从 xsd 自动生成的 DataContract public enum Foo EnumMember Value bar Bar 1 EnumMember Value ba
  • 使用 unrar 库 - 将文件提取到文件流缓冲区中

    我需要的是能够将 rar 文件中的文件提取到流中 我正在创建一个测试用例来了解如何使用解压源文件 http www rarlab com rar unrarsrc 3 9 9 tar gz 我已经搜索和修补了一段时间 但我不知道如何使用该库