{"id":15637024,"url":"https://github.com/tobozo/esp32-targz","last_synced_at":"2025-04-05T09:05:08.009Z","repository":{"id":41445269,"uuid":"236008947","full_name":"tobozo/ESP32-targz","owner":"tobozo","description":"🗜️ An Arduino library to handle tar, gz, and tar.gz files on ESP32, ESP8266 and RP2040, with support for gzip/deflate compression.","archived":false,"fork":false,"pushed_at":"2025-03-21T16:25:20.000Z","size":9368,"stargazers_count":130,"open_issues_count":9,"forks_count":16,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-29T08:04:04.443Z","etag":null,"topics":["arduino","arduino-library","esp32","esp8266","gz","rp2040","rp2040-zero","rp2040w","spiffs","tar","tgz","uncompress","untar","uzlib","zlib"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tobozo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":"https://www.paypal.me/tobozo"}},"created_at":"2020-01-24T13:36:42.000Z","updated_at":"2025-03-22T12:22:41.000Z","dependencies_parsed_at":"2024-03-28T15:36:26.341Z","dependency_job_id":"686c0524-584c-44db-8a5f-47bb0e5eb5d3","html_url":"https://github.com/tobozo/ESP32-targz","commit_stats":{"total_commits":193,"total_committers":4,"mean_commits":48.25,"dds":0.03626943005181349,"last_synced_commit":"2150f37408e61173562f710f2376503692d4330b"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FESP32-targz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FESP32-targz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FESP32-targz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FESP32-targz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tobozo","download_url":"https://codeload.github.com/tobozo/ESP32-targz/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312077,"owners_count":20918344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","arduino-library","esp32","esp8266","gz","rp2040","rp2040-zero","rp2040w","spiffs","tar","tgz","uncompress","untar","uzlib","zlib"],"created_at":"2024-10-03T11:09:38.481Z","updated_at":"2025-04-05T09:05:07.966Z","avatar_url":"https://github.com/tobozo.png","language":"C++","funding_links":["https://www.paypal.me/tobozo"],"categories":[],"sub_categories":[],"readme":"# 🗜️ ESP32-targz\n\n## An ESP32/ESP8266/RP2040 Arduino library to handle .tar, .gz and .tar.gz files\n\n[![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32-targz.svg?)](https://www.ardu-badge.com/ESP32-targz)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/tobozo/library/ESP32-targz.svg)](https://registry.platformio.org/packages/libraries/tobozo/ESP32-targz)\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"ESP32-targz.png\" alt=\"ES32-targz logo\" width=\"512\" /\u003e\n\u003c/p\u003e\n\n## 🆕 ESP32-targz now supports compression!\n\n## ESP32-targz is based on those two great libraries:\n\n  - uzlib https://github.com/pfalcon/uzlib\n  - TinyUntar https://github.com/dsoprea/TinyUntar\n\nESP32-targz enables the channeling of gz :arrow_left::arrow_right: tar :arrow_left::arrow_right: filesystem data in both directions.\n\nParental advisory: this project was made under the influence of hyperfocus and its code may contain comments that are unfit for children.\n\n\nScope\n-----\n\n  - Compressing to `.tar.gz`\n  - Decompressing from `tar.gz` \n  - Compressing to `gz`\n  - Decompressing from `gz` \n  - Packing files/folders to `tar`\n  - Unpacking `tar`\n  - Supports any fs::FS filesystem (SD, SD_MMC, FFat, LittleFS) and Stream (HTTP, HTTPS, UDP, CAN, Ethernet)\n  - This is experimental, expect bugs!\n  - Contributions and feedback are more than welcome :-)\n  \n\nTradeoffs\n---------\n\nWhen decompressing to the filesystem (e.g. NOT when streaming to TAR), gzip can work without the dictionary.\nDisabling the dictionary can cause huge slowdowns but saves ~36KB of ram.\n\nTinyUntar requires 512bytes only so its memory footprint is negligible.\n\n\nLimitations\n-----------\n\n- ESP32-targz decompression can only have one **output** filesystem (see *Support Matrix*), and it must be set at compilation time (see *Usage*).\nThis limitation does not apply to the **input** filesystem/stream.\n\n\n\nSupport Matrix\n--------------\n\n\n| fs::FS  | SPIFFS  |  LittleFS |  SD    |  SD_MMC |  FFAT |\n| ------- |:------- | :-------- | :----- | :------ | :---- |\n| ESP32   | 1.0     |  3.1.0    |  1.0.5 |  1.0    |  1.0  |\n|         |         |           |        |         |       |\n| ESP8266 | builtin |  0.1.0    |  0.1.0 |  n/a    |  n/a  |\n|         |         |           |        |         |       |\n| RP2040  | n/a     |  0.1.0    |  2.0.0 |  n/a    |  n/a  |\n\n\n\nUsage\n-----\n\n\n:warning: Optional: setting the `#define` **before** including `\u003cESP32-targz.h\u003e` will alias a default flash filesystem to `tarGzFS`.\n\n\n```C\n    // Set **destination** filesystem by uncommenting one of these:\n    //#define DEST_FS_USES_SPIFFS\n    //#define DEST_FS_USES_FFAT\n    //#define DEST_FS_USES_SD\n    //#define DEST_FS_USES_SD_MMC\n    #define DEST_FS_USES_LITTLEFS\n    #include \u003cESP32-targz.h\u003e\n    // filesystem object will be available as \"tarGzFS\"\n```\n\n\n\nExtract content from `.gz` file\n-------------------------------\n\n```C\n\n    // mount spiffs (or any other filesystem)\n    tarGzFS.begin();\n\n    GzUnpacker *GZUnpacker = new GzUnpacker();\n\n    GZUnpacker-\u003ehaltOnError( true ); // stop on fail (manual restart/reset required)\n    GZUnpacker-\u003esetupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended\n    GZUnpacker-\u003esetGzProgressCallback( BaseUnpacker::defaultProgressCallback ); // targzNullProgressCallback or defaultProgressCallback\n    GZUnpacker-\u003esetLoggerCallback( BaseUnpacker::targzPrintLoggerCallback  );    // gz log verbosity\n\n    // expand one file\n    if( !GZUnpacker-\u003egzExpander(tarGzFS, \"/gz_example.gz\", tarGzFS, \"/gz_example.jpg\") ) {\n      Serial.printf(\"gzExpander failed with return code #%d\", GZUnpacker-\u003etarGzGetError() );\n    }\n\n    // expand another file\n    if( ! gzExpander(tarGzFS, \"/blah.gz\", tarGzFS, \"/blah.jpg\") ) {\n      Serial.printf(\"operation failed with return code #%d\", GZUnpacker-\u003etarGzGetError() );\n    }\n\n\n```\n\n\nExpand contents from `.tar` file to `/tmp` folder\n-------------------------------------------------\n\n```C\n\n    // mount spiffs (or any other filesystem)\n    tarGzFS.begin();\n\n    TarUnpacker *TARUnpacker = new TarUnpacker();\n\n    TARUnpacker-\u003ehaltOnError( true ); // stop on fail (manual restart/reset required)\n    TARUnpacker-\u003esetTarVerify( true ); // true = enables health checks but slows down the overall process\n    TARUnpacker-\u003esetupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended\n    TARUnpacker-\u003esetTarProgressCallback( BaseUnpacker::defaultProgressCallback ); // prints the untarring progress for each individual file\n    TARUnpacker-\u003esetTarStatusProgressCallback( BaseUnpacker::defaultTarStatusProgressCallback ); // print the filenames as they're expanded\n    TARUnpacker-\u003esetTarMessageCallback( BaseUnpacker::targzPrintLoggerCallback ); // tar log verbosity\n\n    if( !TARUnpacker-\u003etarExpander(tarGzFS, \"/tar_example.tar\", tarGzFS, \"/\") ) {\n      Serial.printf(\"tarExpander failed with return code #%d\\n\", TARUnpacker-\u003etarGzGetError() );\n    }\n\n\n```\n\n\n\nExpand contents from `.tar.gz`  to `/tmp` folder\n------------------------------------------------\n\n```C\n\n    // mount spiffs (or any other filesystem)\n    tarGzFS.begin();\n\n    TarGzUnpacker *TARGZUnpacker = new TarGzUnpacker();\n\n    TARGZUnpacker-\u003ehaltOnError( true ); // stop on fail (manual restart/reset required)\n    TARGZUnpacker-\u003esetTarVerify( true ); // true = enables health checks but slows down the overall process\n    TARGZUnpacker-\u003esetupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended\n    TARGZUnpacker-\u003esetGzProgressCallback( BaseUnpacker::defaultProgressCallback ); // targzNullProgressCallback or defaultProgressCallback\n    TARGZUnpacker-\u003esetLoggerCallback( BaseUnpacker::targzPrintLoggerCallback  );    // gz log verbosity\n    TARGZUnpacker-\u003esetTarProgressCallback( BaseUnpacker::defaultProgressCallback ); // prints the untarring progress for each individual file\n    TARGZUnpacker-\u003esetTarStatusProgressCallback( BaseUnpacker::defaultTarStatusProgressCallback ); // print the filenames as they're expanded\n    TARGZUnpacker-\u003esetTarMessageCallback( BaseUnpacker::targzPrintLoggerCallback ); // tar log verbosity\n\n    // using an intermediate file (default is /tmp/tmp.tar)\n    if( !TARGZUnpacker-\u003etarGzExpander(tarGzFS, \"/targz_example.tar.gz\", tarGzFS, \"/tmp\") ) {\n      Serial.printf(\"tarGzExpander+intermediate file failed with return code #%d\\n\", TARGZUnpacker-\u003etarGzGetError() );\n    }\n\n    // or without intermediate file\n    if( !TARGZUnpacker-\u003etarGzExpander(tarGzFS, \"/targz_example.tar.gz\", tarGzFS, \"/tmp\", nullptr ) ) {\n      Serial.printf(\"tarGzExpander+intermediate file failed with return code #%d\\n\", TARGZUnpacker-\u003etarGzGetError() );\n    }\n\n\n```\n\n\nFlash the ESP with contents from `.gz` file\n-------------------------------------------\n\n```C\n\n    // mount spiffs (or any other filesystem)\n    tarGzFS.begin();\n\n    GzUnpacker *GZUnpacker = new GzUnpacker();\n\n    GZUnpacker-\u003ehaltOnError( true ); // stop on fail (manual restart/reset required)\n    GZUnpacker-\u003esetupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended\n    GZUnpacker-\u003esetGzProgressCallback( BaseUnpacker::defaultProgressCallback ); // targzNullProgressCallback or defaultProgressCallback\n    GZUnpacker-\u003esetLoggerCallback( BaseUnpacker::targzPrintLoggerCallback  );    // gz log verbosity\n\n    if( ! GZUnpacker-\u003egzUpdater(tarGzFS, firmwareFile, U_FLASH,/*don't restart after update*/false ) ) {\n      Serial.printf(\"gzUpdater failed with return code #%d\\n\", GZUnpacker-\u003etarGzGetError() );\n    }\n\n\n```\n\n\nESP32 Only: Flash the ESP with contents from `.gz` stream\n---------------------------------------------------------\n\n```C\n\n    // mount spiffs (or any other filesystem)\n    tarGzFS.begin();\n\n    fs::File file = tarGzFS.open( \"/example_firmware.gz\", \"r\" );\n\n    if (!file) {\n      Serial.println(\"Can't open file\");\n      return;\n    }\n\n    GzUnpacker *GZUnpacker = new GzUnpacker();\n\n    GZUnpacker-\u003ehaltOnError( true ); // stop on fail (manual restart/reset required)\n    GZUnpacker-\u003esetupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended\n    GZUnpacker-\u003esetGzProgressCallback( BaseUnpacker::defaultProgressCallback ); // targzNullProgressCallback or defaultProgressCallback\n    GZUnpacker-\u003esetLoggerCallback( BaseUnpacker::targzPrintLoggerCallback  );    // gz log verbosity\n\n    if( !GZUnpacker-\u003egzStreamUpdater( (Stream *)\u0026file, UPDATE_SIZE_UNKNOWN ) ) {\n      Serial.printf(\"gzStreamUpdater failed with return code #%d\\n\", GZUnpacker-\u003etarGzGetError() );\n    }\n\n\n```\n\n\nESP32 Only: Direct expansion (no intermediate file) from `.tar.gz.` stream\n--------------------------------------------------------------------------\n```C\n\n    // mount spiffs (or any other filesystem)\n    tarGzFS.begin();\n\n    fs::File file = tarGzFS.open( \"/example_archive.tgz\", \"r\" );\n\n    if (!file) {\n      Serial.println(\"Can't open file\");\n      return;\n    }\n\n    TarGzUnpacker *TARGZUnpacker = new TarGzUnpacker();\n\n    TARGZUnpacker-\u003ehaltOnError( true ); // stop on fail (manual restart/reset required)\n    TARGZUnpacker-\u003esetTarVerify( true ); // true = enables health checks but slows down the overall process\n    TARGZUnpacker-\u003esetupFSCallbacks( targzTotalBytesFn, targzFreeBytesFn ); // prevent the partition from exploding, recommended\n    TARGZUnpacker-\u003esetGzProgressCallback( BaseUnpacker::defaultProgressCallback ); // targzNullProgressCallback or defaultProgressCallback\n    TARGZUnpacker-\u003esetLoggerCallback( BaseUnpacker::targzPrintLoggerCallback  );    // gz log verbosity\n    TARGZUnpacker-\u003esetTarProgressCallback( BaseUnpacker::defaultProgressCallback ); // prints the untarring progress for each individual file\n    TARGZUnpacker-\u003esetTarStatusProgressCallback( BaseUnpacker::defaultTarStatusProgressCallback ); // print the filenames as they're expanded\n    TARGZUnpacker-\u003esetTarMessageCallback( BaseUnpacker::targzPrintLoggerCallback ); // tar log verbosity\n\n    if( !TARGZUnpacker-\u003etarGzStreamExpander( (Stream *)\u0026file, tarGzFS ) ) {\n      Serial.printf(\"tarGzStreamExpander failed with return code #%d\\n\", TARGZUnpacker-\u003etarGzGetError() );\n    }\n\n\n```\n\n\nESP32 Only: Direct Update (no intermediate file) from `.tar.gz.` stream\n-----------------------------------------------------------------------\n```C\n\n    TarGzUnpacker *TARGZUnpacker = new TarGzUnpacker();\n\n    TARGZUnpacker-\u003ehaltOnError( true ); // stop on fail (manual restart/reset required)\n    TARGZUnpacker-\u003esetTarVerify( false ); // nothing to verify as we're writing a partition\n    TARGZUnpacker-\u003esetGzProgressCallback( BaseUnpacker::targzNullProgressCallback ); // don't care about gz progress\n    TARGZUnpacker-\u003esetTarProgressCallback( BaseUnpacker::defaultProgressCallback ); // prints the untarring progress for each individual partition\n    TARGZUnpacker-\u003esetTarStatusProgressCallback( BaseUnpacker::defaultTarStatusProgressCallback ); // print the filenames as they're expanded\n    TARGZUnpacker-\u003esetTarMessageCallback( myTarMessageCallback/*BaseUnpacker::targzPrintLoggerCallback*/ ); // tar log verbosity\n\n    // mount SD\n    SD.begin();\n\n    // this .tar.gz file has both the \"app.ino.bin\" and \"app.spiffs.bin\" partitions\n    fs::File file = SD.open( \"/bundle_firmware.tar.gz\", \"r\" );\n\n    if (!file) {\n      Serial.println(\"Can't open file\");\n      return;\n    }\n\n    // this could also be a HTTP/HTTPS/UDP/Ethernet Stream\n    Stream *streamptr = \u0026file;\n\n    if( !TARGZUnpacker-\u003etarGzStreamUpdater( streamptr ) ) {\n      Serial.printf(\"tarGzStreamUpdater failed with return code #%d\\n\", TARGZUnpacker-\u003etarGzGetError() );\n    } else {\n      Serial.println( \"Flashing successful, now restarting\" );\n      ESP.restart();\n    }\n\n\n```\n\n\nLZPacker::compress() signatures:\n-------------------------------\n```cpp\n  // buffer to stream (best compression)\n  size_t compress( uint8_t* srcBuf, size_t srcBufLen, Stream* dstStream );\n  // buffer to buffer (best compression)\n  size_t compress( uint8_t* srcBuf, size_t srcBufLen, uint8_t** dstBufPtr );\n  // stream to buffer\n  size_t compress( Stream* srcStream, size_t srcLen, uint8_t** dstBufPtr );\n  // stream to stream\n  size_t compress( Stream* srcStream, size_t srcLen, Stream* dstStream );\n  // stream to file\n  size_t compress( Stream* srcStream, size_t srcLen, fs::FS*dstFS, const char* dstFilename );\n  // file to file\n  size_t compress( fs::FS *srcFS, const char* srcFilename, fs::FS*dstFS, const char* dstFilename );\n  // file to stream\n  size_t compress( fs::FS *srcFS, const char* srcFilename, Stream* dstStream );\n```\n\nCompress to `.gz` (buffer to stream)\n-------------------------------\n\n```C\n    const char* json = \"{\\\"hello\\\":\\\"world\\\"}\"; // input buffer\n    File out = LittleFS.open(\"/out.gz\", \"w\");   // output stream\n    size_t compressedSize = LZPacker::compress( (uint8_t*)json, strlen(json), \u0026out );\n    out.close();\n```\n\nCompress to `.gz` (buffer to buffer)\n-------------------------------\n\n```C\n    const char* json = \"{\\\"hello\\\":\\\"world\\\"}\"; // input buffer\n    uint8_t* compressedBytes;                   // output buffer\n    size_t compressedSize = LZPacker::compress( (uint8_t*)json, strlen(json), \u0026compressedBytes);\n    // do something with compressedBytes\n    free(compressedBytes);\n```\n\nCompress to `.gz` (stream to buffer)\n-------------------------------\n\n```C\n    File in = LittleFS.open(\"/my.uncompressed.file.txt\"); // input stream\n    uint8_t* compressedBytes;                             // output buffer\n    size_t compressedSize = LZPacker::compress( \u0026in, in.size(), \u0026compressedBytes );\n    // do something with compressedBytes\n    free(compressedBytes);\n    in.close();\n```\n\nCompress to `.gz` (stream to stream)\n-------------------------------\n\n```C\n    File in = LittleFS.open(\"/my.uncompressed.file.txt\"); // input stream\n    File out = LittleFS.open(\"/out.gz\", \"w\");             // output stream\n    size_t compressedSize = LZPacker::compress( \u0026in, in.size(), \u0026out );\n    out.close();\n    in.close();\n```\n    \n\nTarPacker::pack_files() signatures:\n-------------------------------\n```cpp\n  int pack_files(fs::FS *srcFS, std::vector\u003cdir_entity_t\u003e dirEntities, Stream* dstStream, const char* tar_prefix=nullptr);\n  int pack_files(fs::FS *srcFS, std::vector\u003cdir_entity_t\u003e dirEntities, fs::FS *dstFS, const char*tar_output_file_path, const char* tar_prefix=nullptr);\n```\n    \n    \nPack to `.tar` (entities to File)\n-------------------------------\n```C\n  std::vector\u003cTAR::dir_entity_t\u003e dirEntities;\n  TarPacker::collectDirEntities(\u0026dirEntities, \u0026LittleFS, \"/folder/to/pack\");\n  auto packedSize = TarPacker::pack_files(\u0026LittleFS, dirEntities, \u0026LittleFS, \"/my.archive.tar\");\n```\n  \nPack to `.tar` (entities to Stream)\n-------------------------------\n```C\n  std::vector\u003cTAR::dir_entity_t\u003e dirEntities;\n  TarPacker::collectDirEntities(\u0026dirEntities, \u0026LittleFS, \"/folder/to/pack\");\n  File tarOutfile = LittleFS.open(\"/my.archive.tar\", \"w\");  \n  size_t packedSize = TarPacker::pack_files(\u0026LittleFS, dirEntities, \u0026tarOutfile);\n  tarOutfile.close();\n```\n  \n  \nTarGzPacker::compress() signatures:\n-------------------------------  \n\n```cpp\n  int compress(fs::FS *srcFS, const char* srcDir, Stream* dstStream, const char* tar_prefix=nullptr);\n  int compress(fs::FS *srcFS, const char* srcDir, fs::FS *dstFS, const char* tgz_name, const char* tar_prefix=nullptr);\n  \n  int compress(fs::FS *srcFS, std::vector\u003cdir_entity_t\u003e dirEntities, Stream* dstStream, const char* tar_prefix=nullptr);\n  int compress(fs::FS *srcFS, std::vector\u003cdir_entity_t\u003e dirEntities, fs::FS *dstFS, const char* tgz_name, const char* tar_prefix=nullptr);\n```\n  \n  \nPack \u0026 compress to `.tar.gz` file/stream (no filtering on source files/folders list, recursion applies)\n-------------------------------  \n```C\n  File TarGzOutFile = LittleFS.open(\"/my.archive.tar.gz\", \"w\");\n  size_t compressedSize = TarGzPacker::compress(\u0026LittleFS/*source*/, \"/folder/to/compress\", \u0026TarGzOutFile);\n  TarGzOutFile.close();\n```\n\nPack \u0026 compress to `.tar.gz` file/stream\n-------------------------------  \n\n```C\n  std::vector\u003cTAR::dir_entity_t\u003e dirEntities;\n  TarPacker::collectDirEntities(\u0026dirEntities, \u0026LittleFS/*source*/, \"/folder/to/compress\");\n  // eventually filter content from dirEntities\n  File TarGzOutFile = LittleFS.open(\"/my.archive.tar.gz\", \"w\");\n  size_t compressedSize = TarGzPacker::compress(\u0026LittleFS/*source*/, dirEntities, \u0026TarGzOutFile);\n  TarGzOutFile.close();\n```\n\nPack \u0026 compress to `.tar.gz` file (no filtering on source files/folders list, recursion applies)\n-------------------------------  \n```C\n  File TarGzOutFile = LittleFS.open(\"/my.archive.tar.gz\", \"w\");\n  size_t compressedSize = TarGzPacker::compress(\u0026LittleFS/*source*/, \"/folder/to/compress\", \u0026LittleFS/*destination*/, \"/my.archive.tar.gz\");\n  TarGzOutFile.close();\n```\n\n\nPack \u0026 compress to `.tar.gz` file\n-------------------------------  \n\n```C\n  std::vector\u003cTAR::dir_entity_t\u003e dirEntities;\n  TarPacker::collectDirEntities(\u0026dirEntities, \u0026LittleFS/*source*/, \"/folder/to/compress\");\n  // eventually filter content from dirEntities\n  File TarGzOutFile = LittleFS.open(\"/my.archive.tar.gz\", \"w\");\n  size_t compressedSize = TarGzPacker::compress(\u0026LittleFS/*source*/, dirEntities, \u0026LittleFS/*destination*/, \"/my.archive.tar.gz\");\n  TarGzOutFile.close();\n```\n  \n\n\n\n\n  \n\n\nTarGzUnpacker/GzUnpacker/TarUnpacker Callbacks\n---------\n\n```C\n\n\n    // basic progress callback (valid for tar or gzip)\n    void myBasicProgressCallback( uint8_t progress )\n    {\n      Serial.printf(\"Progress: %d\\n\", progress );\n    }\n\n\n    // complex progress callback (valid for tar or gzip)\n    void myProgressCallback( uint8_t progress )\n    {\n      static int8_t myLastProgress = -1;\n      if( myLastProgress != progress ) {\n        if( myLastProgress == -1 ) {\n          Serial.print(\"Progress: \");\n        }\n        myLastProgress = progress;\n        switch( progress ) {\n          case   0: Serial.print(\"0% ▓\");  break;\n          case  25: Serial.print(\" 25% \");break;\n          case  50: Serial.print(\" 50% \");break;\n          case  75: Serial.print(\" 75% \");break;\n          case 100: Serial.print(\"▓ 100%\\n\"); myLastProgress = -1; break;\n          default: if( progress \u003c 100) Serial.print( \"▓\" ); break;\n        }\n      }\n    }\n\n\n    // General Error/Warning/Info logger\n    void myLogger(const char* format, ...)\n    {\n      va_list args;\n      va_start(args, format);\n      vprintf(format, args);\n      va_end(args);\n    }\n\n\n    // status callback for TAR (fired at file creation)\n    void myTarStatusProgressCallback( const char* name, size_t size, size_t total_unpacked )\n    {\n      Serial.printf(\"[TAR] %-64s %8d bytes - %8d Total bytes\\n\", name, size, total_unpacked );\n    }\n\n\n\n```\n\nTarGzUnpacker/GzUnpacker/TarUnpacker Return Codes \n------------\n\n`*Unpacker-\u003etarGzGetError()` returns a value when a problem occured:\n\n  - General library error codes\n\n    - `0`    : Yay no error!\n    - `-1`   : Filesystem error\n    - `-6`   : Same a Filesystem error\n    - `-7`   : Update not finished? Something went wrong\n    - `-38`  : Logic error during deflating\n    - `-39`  : Logic error during gzip read\n    - `-40`  : Logic error during file creation\n    - `-100` : No space left on device\n    - `-101` : No space left on device\n    - `-102` : No space left on device\n    - `-103` : Not enough heap\n    - `-104` : Gzip dictionnary needs to be enabled\n    - `-105` : Gz Error when parsing header\n    - `-106` : Gz Error when allocating memory\n    - `-107` : General error, file integrity check fail\n\n  - UZLIB: forwarding error values from uzlib.h as is (no offset)\n\n    - `-2`   : Not a valid gzip file\n    - `-3`   : Gz Error TINF_DATA_ERROR\n    - `-4`   : Gz Error TINF_CHKSUM_ERROR\n    - `-5`   : Gz Error TINF_DICT_ERROR\n    - `-41`  : Gz error, can't guess filename\n\n  - UPDATE: applying -20 offset to forwarded error values from Update.h\n\n    - `-8`   : Updater Error UPDATE_ERROR_ABORT\n    - `-9`   : Updater Error UPDATE_ERROR_BAD_ARGUMENT\n    - `-10`  : Updater Error UPDATE_ERROR_NO_PARTITION\n    - `-11`  : Updater Error UPDATE_ERROR_ACTIVATE\n    - `-12`  : Updater Error UPDATE_ERROR_MAGIC_BYTE\n    - `-13`  : Updater Error UPDATE_ERROR_MD5\n    - `-14`  : Updater Error UPDATE_ERROR_STREAM\n    - `-15`  : Updater Error UPDATE_ERROR_SIZE\n    - `-16`  : Updater Error UPDATE_ERROR_SPACE\n    - `-17`  : Updater Error UPDATE_ERROR_READ\n    - `-18`  : Updater Error UPDATE_ERROR_ERASE\n    - `-19`  : Updater Error UPDATE_ERROR_WRITE\n\n  - TAR: applying -30 offset to forwarded error values from untar.h\n\n    - `32`  : Tar Error TAR_ERR_DATACB_FAIL\n    - `33`  : Tar Error TAR_ERR_HEADERCB_FAIL\n    - `34`  : Tar Error TAR_ERR_FOOTERCB_FAIL\n    - `35`  : Tar Error TAR_ERR_READBLOCK_FAIL\n    - `36`  : Tar Error TAR_ERR_HEADERTRANS_FAIL\n    - `37`  : Tar Error TAR_ERR_HEADERPARSE_FAIL\n    - `38`  : Tar Error TAR_ERROR_HEAP\n\n\n\nTest Suite\n----------\n\n  - 📷 [ESP32 capture](extras/esp32-test-suite.gif)\n  - 📷 [ESP8266 capture](extras/esp8266-test-suite.gif)\n\n\nKnown bugs\n----------\n\n  - SPIFFS is deprecated, migrate to LittleFS!\n  - tarGzExpander/tarExpander: symlinks or long filename/path not supported, path limit is 100 chars\n  - tarGzExpander without intermediate file uses a lot of heap\n  - tarGzExpander/gzExpander on ESP8266 : while the provided examples will work, the 32Kb dynamic allocation for gzip dictionary is unlikely to work in real world scenarios (e.g. with a webserver) and would probably require static allocation\n\n\n\nDebugging:\n----------\n\n  - ESP32: use all of the \"Debug level\" values from the boards menu\n  - ESP8266: Warning/Error when \"Debug Port:Serial\" is used, and Debug/Verbose when \"Debug Level:Core\" is selected from the boards menu\n  - RP2040: only \"Debug port: Serial\" and \"Debug Level: Core\" enable logging\n\n\nResources\n-----------\n  - [ESP8266 Sketch Data Upload tool for LittleFS](https://github.com/earlephilhower/arduino-esp8266littlefs-plugin)\n  - [ESP32 Sketch Data Upload tool for FFat/LittleFS/SPIFFS](https://github.com/lorol/arduino-esp32fs-plugin/releases)\n  - [Pico LittlsFS Data Upload tool](https://github.com/earlephilhower/arduino-pico-littlefs-plugin)\n\n  ![image](https://user-images.githubusercontent.com/1893754/99714053-635de380-2aa5-11eb-98e3-631a94836742.png)\n\n\nAlternate links\n---------------\n  - [https://github.com/vortigont/esp32-flashz](https://github.com/vortigont/esp32-flashz) OTA-Update your ESP32 from zlib compressed binaries (not gzip)\n  - [https://github.com/chrisjoyce911/esp32FOTA](https://github.com/chrisjoyce911/esp32FOTA) OTA-Update your ESP32 from zlib or gzip compressed binaries\n  - [https://github.com/laukik-hase/esp_compression](https://github.com/laukik-hase/esp_compression) inflate/deflate miniz/uzlib based esp-idf implementation\n\nCredits:\n--------\n  - [pfalcon](https://github.com/pfalcon/uzlib) (uzlib maintainer)\n  - [dsoprea](https://github.com/dsoprea/TinyUntar) (TinyUntar maintainer)\n  - [lorol](https://github.com/lorol) (LittleFS-ESP32 + fs plugin)\n  - [me-no-dev](https://github.com/me-no-dev) (inspiration and support)\n  - [atanisoft](https://github.com/atanisoft) (motivation and support)\n  - [lbernstone](https://github.com/lbernstone) (motivation and support)\n  - [scubachristopher](https://github.com/scubachristopher) (contribution and support)\n  - [infrafast](https://github.com/infrafast) (feedback fueler)\n  - [vortigont](https://github.com/vortigont/) (inspiration and support)\n  - [hitecSmartHome](https://github.com/hitecSmartHome) (feedback fueler)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobozo%2Fesp32-targz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftobozo%2Fesp32-targz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobozo%2Fesp32-targz/lists"}