2010-02-24 22 views
7

Điều tôi cần là có thể trích xuất tệp trong tệp .rar thành luồng. Tôi đang tạo một trường hợp thử nghiệm để biết cách sử dụng unrar source. Tôi đã tìm kiếm và sửa đổi một lúc, nhưng tôi không thể tìm ra cách sử dụng thư viện. Tôi ngạc nhiên vì tôi thậm chí không thể tìm thấy tài liệu hoặc hướng dẫn cho nó, xem xét cách phổ biến .rar lưu trữ.Sử dụng thư viện unrar - giải nén tệp vào bộ đệm dòng phim

Tôi đã tự thực hiện một chút, nhưng không phải lúc nào cũng hoạt động. Một số tệp được trích xuất đúng cách. Các tệp khác bị lộn xộn vì một số lý do (nhưng không phải là hoàn toàn dữ liệu nhị phân "rác"). Tất cả những gì tôi biết cho đến nay là, thường (nhưng không phải lúc nào):

  • không có tệp đang hoạt động có fileInfo.Method = 48. Họ dường như file đó có một tỷ lệ nén 100% - tức là không nén

  • file làm việc có fileInfo.Method = 49, 50, 51, 52, hoặc 53, tương ứng với tốc độ nén, nhanh nhất, Fast, Normal, Tốt , Tốt nhất

Nhưng tôi không biết tại sao lại thế. Vẫn không thể tìm thấy tài liệu hoặc ví dụ hoạt động.

Dưới đây là nguồn trường hợp thử nghiệm mà tôi có cho đến nay và example rar archive rằng, khi được trích xuất bằng chương trình này, có cả tệp hoạt động và không hoạt động.

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

Tôi đã tìm thấy một tập tin đó dường như là (tuyên bố là?) Các documentation, nhưng theo các tập tin, tôi không làm điều gì sai trái. Tôi nghĩ rằng tôi có thể bị buộc phải nghỉ mát để CRC kiểm tra bộ đệm và thực hiện một workaround nếu nó không thành công.

nguồn giải pháp (nhờ, 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; 

} 
+0

Có thể có sự cố với các ký tự EOL (lưu trữ được tạo trên Windows nhưng được trích xuất trên Unix), nhưng tôi không chắc chắn .. –

+0

Tôi đảm bảo sử dụng đúng 'buffLen' hoặc' fileSize' khi đọc/ghi vào bộ đệm mặc dù. Tại thời điểm này, tôi đã sẵn sàng để chỉ đổ lỗi cho thư viện unrar. – Kache

Trả lời

6

Tôi không quen với unrar, sau khi đọc nhanh tài liệu, tôi nghĩ bạn đang giả định rằng CallbackProc được gọi chính xác một lần cho mỗi tệp. Tuy nhiên, tôi nghĩ rằng unrar có thể gọi nó nhiều lần. Nó giải nén một số dữ liệu rồi gọi CallbackProc, sau đó mở gói dữ liệu tiếp theo và gọi lại CallbackProc, quá trình này được lặp lại cho đến khi tất cả dữ liệu được xử lý. Bạn nên nhớ có bao nhiêu byte thực sự được ghi vào bộ đệm và nối thêm dữ liệu mới với độ lệch tương ứng.

+0

Nó chắc chắn giải thích lý do tại sao các tập tin trích xuất thất bại đã lộn xộn nhưng không phải tất cả dữ liệu rác. Tôi đọc lại tài liệu, và nó không cho tôi ấn tượng rằng unrar có thể thực hiện gọi lại nhiều lần cho mỗi tập tin. Bạn nghĩ thế nào về nó? Có một cuộc gọi lại thực hiện định kỳ ở giữa khai thác không có vẻ rất trực quan với tôi. – Kache

+0

Tôi đoán lý do là các tệp trong kho lưu trữ có thể đủ lớn và không vừa với bộ nhớ có sẵn. Việc giải nén các tệp như vậy hoàn toàn vào một bộ đệm sẽ là không thể hoặc ít nhất là không hiệu quả. –

0

Bạn dường như đã đăng tải một số mã nguồn, nhưng không có câu hỏi thực tế.

có bạn xem xét Rarlabs Feedback Page (mà điểm đến của họ forums

Ngoài ra, xem: This Article

+0

Tôi không thể nhận được (ngay cả một trống 'chính()') để biên dịch. Tôi đang cố gắng giải nén một tập tin trong một kho lưu trữ rar vào một bộ đệm, để làm với như tôi muốn. Tôi đã xem qua các liên kết của bạn. Rarlabs không có hỗ trợ cho thư viện unrar của họ mà tôi biết - chỉ là một thư viện unrar có nguồn mở không có tài liệu. – Kache

3

tôi không thể tìm thấy bất kỳ tài liệu trực tuyến, hoặc, nhưng có ví dụ bạn có thể sử dụng:

Đi đến http://www.krugle.com và ở góc dưới bên trái của trang, nhập từ khóa như RAROpenArchiveEx. Bạn sẽ thấy các tệp tiêu đề và nguồn từ nhiều nguồn mở projec ts sử dụng thư viện unrar.

Điều đó sẽ giúp bạn tiếp tục.

+0

Cảm ơn! Tôi sẽ xem qua một số trong số này. Hy vọng rằng ít nhất một trong những chất chiết xuất này trực tiếp đến một bộ đệm, và tôi có thể tìm ra cách để làm điều tương tự. – Kache

Các vấn đề liên quan