2010-02-24 6 views
7

필요한 것은 .rar 파일의 파일을 스트림으로 추출 할 수 있어야합니다. unrar source을 사용하는 방법에 대한 이해를 얻으려고 테스트 케이스를 만듭니다. 나는 잠시 동안 찾고 있었지만 라이브러리를 사용하는 방법을 알 수 없다. 나는 .rar 아카이브가 얼마나 일반적인지를 고려하여 문서 또는 튜토리얼을 찾을 수 없다는 것에 놀랐다.unrar 라이브러리 사용 - 파일 스트림 버퍼로 파일 압축

나는 약간의 진전을 보였지만 항상 효과가있는 것은 아닙니다. 특정 파일이 제대로 추출됩니다. 어떤 이유로 든 다른 파일들이 뒤죽박죽이됩니다 (은 완전히 "쓰레기"이진 데이터가 아닙니다). 모든 내가 알고 지금까지이며, 보통 (항상은 아니지만) :

  • 작동하지 않는 파일을 fileInfo.Method = 48 있습니다. 그들은 100 %의 압축 비율을 가지고 할 파일을 표시 - fileInfo.Method = 49, 50, 압축 속도에 해당 51, 52, 또는 53, 가장 빠른, 고속, 일반, 좋은

  • 작업 파일이 더 압축 즉 없습니다 , 베스트

그러나 나는 그것이 왜 있는지 모른다. 아직도 문서 또는 실제 예제를 찾을 수 없습니다.

아래에는이 프로그램으로 추출 할 때 작동하는 파일과 작동하지 않는 파일이 모두 포함되어 있으며 지금까지 가지고있는 테스트 사례 소스는 example rar archive입니다.

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

} 

업데이트 : 것으로 보인다 (? 주장)을 documentation을 파일을 발견했습니다,하지만 파일에 따라, 내가 잘못 아무것도 아니에요

. 버퍼를 검사하는 CRC를 사용하고 실패 할 경우 대안을 구현해야 할 수도 있습니다.

솔루션 소스 (덕분에, 데니스 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; 

} 
+0

어쩌면 EOL 문자 (Windows에서는 만들었지 만 유닉스에서 추출한 파일)에는 문제가있을 수 있지만 확실하지는 않습니다. –

+0

읽을 때 올바른 'buffLen' 또는'fileSize'를 사용해야합니다./버퍼에 쓰기. 이 시점에서 나는 비난당한 라이브러리에 책임을 묻기 만하려고합니다. – Kache

답변

6

필자는 문서를 빨리 ​​읽은 후에 CallbackProc이 파일 당 정확히 한 번 호출된다고 생각한다고 생각합니다. 그러나, 나는 unrar가 여러 번 그것을 부를지도 모른다라고 생각한다. 일부 데이터의 압축을 푼 다음 CallbackProc을 호출하고 다음 데이터 청크를 풀고 CallbackProc을 다시 호출하면 모든 데이터가 처리 될 때까지 프로세스가 반복됩니다. 실제로 얼마나 많은 바이트가 버퍼에 쓰여지고 해당 오프셋에 새 데이터를 추가하는지 기억해야합니다.

+0

실패한 추출 파일이 왜 혼란스럽지 만 모든 가비지 데이터가 왜 그런지는 분명히 설명합니다. 문서를 다시 읽었을 때 파일 당 콜백을 여러 번 실행할 수 있다는 인상을주지 못했습니다. 어떻게 생각하니? 추출 중간에 주기적으로 콜백을 실행하는 것은 나에게 매우 직관적이지 않습니다. – Kache

+0

그 이유는 아카이브에있는 파일이 충분히 커서 사용 가능한 메모리에 맞지 않는다는 것입니다. 이러한 파일을 하나의 버퍼로 완전히 압축하는 것은 불가능하거나 적어도 비효율적입니다. –

0

당신은 보이는 약간의 소스 코드,하지만 실제 질문을 게시합니다. 이 This Article

+0

컴파일 할 때 (심지어 비어있는'main()')를 얻을 수 없습니다. 내가 원하는대로 할 버퍼에 rar 보관 파일의 압축을 풀려고 해요. 나는 너의 연결 고리를 들여다 보았다. Rarlabs는 내가 알고있는 unrar 라이브러리를 지원하지 않습니다. 문서가없는 오픈 소스 라이브러리입니다. – Kache

3

나도 온라인 어떤 문서를 찾을 수 있지만, 당신이 사용할 수있는사례가 :

당신이 볼 (또한 자신의 forums

을 가리키는 Rarlabs Feedback Page보고 생각 해 봤나 :

http://www.krugle.com으로 이동하고 RAROpenArchiveEx과 같은 키워드를 입력하면 다양한 오픈 소스 프로젝트의 헤더 및 소스 파일이 표시됩니다. unrar 라이브러리를 사용합니다.

그러면 가야합니다.

+0

고마워요! 이 중 일부를 살펴 보겠습니다. 이 추출물 중 적어도 하나가 버퍼에 직접 전달되기를 바랍니다. 동일한 작업을 수행하는 방법을 알아낼 수 있습니다. – Kache