diff --git a/coding/coding_tests/zip_reader_test.cpp b/coding/coding_tests/zip_reader_test.cpp index c533fa029d..ac004020e3 100644 --- a/coding/coding_tests/zip_reader_test.cpp +++ b/coding/coding_tests/zip_reader_test.cpp @@ -127,3 +127,57 @@ UNIT_TEST(ZipFilesList) FileWriter::DeleteFileX(ZIPFILE_INVALID); FileWriter::DeleteFileX(ZIPFILE); } + +/// Compressed zip file with 2 files in assets folder: +/// assets/aaaaaaaaaa.txt (contains text "aaaaaaaaaa\x0A") +/// assets/holalala.txt (contains text "Holalala\x0A") +static char const zipBytes3[] = \ + "\x50\x4B\x03\x04\x14\x00\x02\x00\x08\x00\xAF\x96\x56\x40\x42\xE5\x26\x8F\x06\x00" \ + "\x00\x00\x0B\x00\x00\x00\x15\x00\x1C\x00\x61\x73\x73\x65\x74\x73\x2F\x61\x61\x61" \ + "\x61\x61\x61\x61\x61\x61\x61\x2E\x74\x78\x74\x55\x54\x09\x00\x03\x7A\x0F\x45\x4F" \ + "\xD8\x0F\x45\x4F\x75\x78\x0B\x00\x01\x04\xF5\x01\x00\x00\x04\x14\x00\x00\x00\x4B" \ + "\x4C\x84\x01\x2E\x00\x50\x4B\x03\x04\x14\x00\x02\x00\x08\x00\xE6\x96\x56\x40\x5E" \ + "\x76\x90\x07\x08\x00\x00\x00\x09\x00\x00\x00\x13\x00\x1C\x00\x61\x73\x73\x65\x74" \ + "\x73\x2F\x68\x6F\x6C\x61\x6C\x61\x6C\x61\x2E\x74\x78\x74\x55\x54\x09\x00\x03\xDF" \ + "\x0F\x45\x4F\xDC\x0F\x45\x4F\x75\x78\x0B\x00\x01\x04\xF5\x01\x00\x00\x04\x14\x00" \ + "\x00\x00\xF3\xC8\xCF\x49\x04\x41\x2E\x00\x50\x4B\x01\x02\x1E\x03\x14\x00\x02\x00" \ + "\x08\x00\xAF\x96\x56\x40\x42\xE5\x26\x8F\x06\x00\x00\x00\x0B\x00\x00\x00\x15\x00" \ + "\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xA4\x81\x00\x00\x00\x00\x61\x73\x73\x65" \ + "\x74\x73\x2F\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x2E\x74\x78\x74\x55\x54\x05" \ + "\x00\x03\x7A\x0F\x45\x4F\x75\x78\x0B\x00\x01\x04\xF5\x01\x00\x00\x04\x14\x00\x00" \ + "\x00\x50\x4B\x01\x02\x1E\x03\x14\x00\x02\x00\x08\x00\xE6\x96\x56\x40\x5E\x76\x90" \ + "\x07\x08\x00\x00\x00\x09\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00" \ + "\x00\xA4\x81\x55\x00\x00\x00\x61\x73\x73\x65\x74\x73\x2F\x68\x6F\x6C\x61\x6C\x61" \ + "\x6C\x61\x2E\x74\x78\x74\x55\x54\x05\x00\x03\xDF\x0F\x45\x4F\x75\x78\x0B\x00\x01" \ + "\x04\xF5\x01\x00\x00\x04\x14\x00\x00\x00\x50\x4B\x05\x06\x00\x00\x00\x00\x02\x00" \ + "\x02\x00\xB4\x00\x00\x00\xAA\x00\x00\x00\x00\x00"; + +UNIT_TEST(ZipExtract) +{ + string const ZIPFILE = "test.zip"; + { + FileWriter f(ZIPFILE); + f.Write(zipBytes3, ARRAY_SIZE(zipBytes3)); + } + TEST(ZipFileReader::IsZip(ZIPFILE), ("Not a zip file")); + + vector files = ZipFileReader::FilesList(ZIPFILE); + TEST_EQUAL(files.size(), 2, ()); + + string const OUTFILE = "out.tmp"; + string s; + ZipFileReader::UnzipFile(ZIPFILE, files[0], OUTFILE); + { + FileReader(OUTFILE).ReadAsString(s); + } + TEST_EQUAL(s, "aaaaaaaaaa\x0A", ()); + // OUTFILE should be rewritten correctly in the next lines + ZipFileReader::UnzipFile(ZIPFILE, files[1], OUTFILE); + { + FileReader(OUTFILE).ReadAsString(s); + } + TEST_EQUAL(s, "Holalala\x0A", ()); + FileWriter::DeleteFileX(OUTFILE); + + FileWriter::DeleteFileX(ZIPFILE); +} diff --git a/coding/zip_reader.cpp b/coding/zip_reader.cpp index 33d81eed3d..163e6aea65 100644 --- a/coding/zip_reader.cpp +++ b/coding/zip_reader.cpp @@ -3,6 +3,8 @@ #include "../base/scope_guard.hpp" #include "../base/logging.hpp" +#include "../coding/file_writer.hpp" + #include "../std/bind.hpp" #include "../3party/zlib/contrib/minizip/unzip.h" @@ -73,3 +75,45 @@ bool ZipFileReader::IsZip(string const & zipContainer) unzClose(zip); return true; } + +void ZipFileReader::UnzipFile(string const & zipContainer, string const & fileInZip, + string const & outFilePath) +{ + unzFile zip = unzOpen64(zipContainer.c_str()); + if (!zip) + MYTHROW(OpenZipException, ("Can't get zip file handle", zipContainer)); + MY_SCOPE_GUARD(zipGuard, bind(&unzClose, zip)); + + if (UNZ_OK != unzLocateFile(zip, fileInZip.c_str(), 1)) + MYTHROW(LocateZipException, ("Can't locate file inside zip", fileInZip)); + + if (UNZ_OK != unzOpenCurrentFile(zip)) + MYTHROW(LocateZipException, ("Can't open file inside zip", fileInZip)); + MY_SCOPE_GUARD(currentFileGuard, bind(&unzCloseCurrentFile, zip)); + + try + { + FileWriter outFile(outFilePath); + + int readBytes; + static size_t const BUF_SIZE = 4096; + char buf[BUF_SIZE]; + while (true) + { + readBytes = unzReadCurrentFile(zip, buf, BUF_SIZE); + if (readBytes > 0) + outFile.Write(buf, static_cast(readBytes)); + else if (readBytes < 0) + MYTHROW(InvalidZipException, ("Error", readBytes, "while unzipping", fileInZip, "from", zipContainer)); + else + break; + } + } + catch (Exception const & e) + { + // Delete unfinished output file + FileWriter::DeleteFileX(outFilePath); + // Rethrow exception - we've failed + throw; + } +} diff --git a/coding/zip_reader.hpp b/coding/zip_reader.hpp index 031b2bd3da..a107bc95c0 100644 --- a/coding/zip_reader.hpp +++ b/coding/zip_reader.hpp @@ -20,6 +20,10 @@ public: ZipFileReader(string const & container, string const & file); + // Can also throw Writer::OpenException and Writer::WriteException + static void UnzipFile(string const & zipContainer, string const & fileInZip, + string const & outFilePath); + static vector FilesList(string const & zipContainer); /// Quick version without exceptions static bool IsZip(string const & zipContainer);