Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork17
🗜️ An Arduino library to compress/decompress tar, gz, and tar.gz files on ESP32, ESP8266 and RP2040
License
tobozo/ESP32-targz
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ESP32-targz enables the channeling of gz ⬅️➡️ tar ⬅️➡️ filesystem data in both directions.
Parental advisory: this project was made under the influence of hyperfocus and its code may contain comments that are unfit for children.
- Compressing to
.tar.gz
- Decompressing from
tar.gz
- Compressing to
gz
- Decompressing from
gz
- Packing files/folders to
tar
- Unpacking
tar
- Supports any fs::FS filesystem (SD, SD_MMC, FFat, LittleFS) and Stream (HTTP, HTTPS, UDP, CAN, Ethernet)
- This is experimental, expect bugs!
- Contributions and feedback are more than welcome :-)
When decompressing to the filesystem (e.g. NOT when streaming to TAR), gzip can work without the dictionary.Disabling the dictionary can cause huge slowdowns but saves ~36KB of ram.
TinyUntar requires 512bytes only so its memory footprint is negligible.
- ESP32-targz decompression can only have oneoutput filesystem (seeSupport Matrix), and it must be set at compilation time (seeUsage).This limitation does not apply to theinput filesystem/stream.
fs::FS | SPIFFS | LittleFS | SD | SD_MMC | FFAT |
---|---|---|---|---|---|
ESP32 | 1.0 | 3.1.0 | 1.0.5 | 1.0 | 1.0 |
ESP8266 | builtin | 0.1.0 | 0.1.0 | n/a | n/a |
RP2040 | n/a | 0.1.0 | 2.0.0 | n/a | n/a |
#define
before including<ESP32-targz.h>
will alias a default flash filesystem totarGzFS
.
// Set **destination** filesystem by uncommenting one of these://#define DEST_FS_USES_SPIFFS//#define DEST_FS_USES_FFAT//#define DEST_FS_USES_SD//#define DEST_FS_USES_SD_MMC#defineDEST_FS_USES_LITTLEFS#include<ESP32-targz.h>// filesystem object will be available as "tarGzFS"
// mount spiffs (or any other filesystem)tarGzFS.begin();GzUnpacker*GZUnpacker=newGzUnpacker();GZUnpacker->haltOnError( true );// stop on fail (manual restart/reset required)GZUnpacker->setupFSCallbacks(targzTotalBytesFn,targzFreeBytesFn );// prevent the partition from exploding, recommendedGZUnpacker->setGzProgressCallback(BaseUnpacker::defaultProgressCallback );// targzNullProgressCallback or defaultProgressCallbackGZUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback );// gz log verbosity// expand one fileif( !GZUnpacker->gzExpander(tarGzFS,"/gz_example.gz",tarGzFS,"/gz_example.jpg") ) {Serial.printf("gzExpander failed with return code #%d",GZUnpacker->tarGzGetError() ); }// expand another fileif( !gzExpander(tarGzFS,"/blah.gz",tarGzFS,"/blah.jpg") ) {Serial.printf("operation failed with return code #%d",GZUnpacker->tarGzGetError() ); }
// mount spiffs (or any other filesystem)tarGzFS.begin();TarUnpacker*TARUnpacker=newTarUnpacker();TARUnpacker->haltOnError( true );// stop on fail (manual restart/reset required)TARUnpacker->setTarVerify( true );// true = enables health checks but slows down the overall processTARUnpacker->setupFSCallbacks(targzTotalBytesFn,targzFreeBytesFn );// prevent the partition from exploding, recommendedTARUnpacker->setTarProgressCallback(BaseUnpacker::defaultProgressCallback );// prints the untarring progress for each individual fileTARUnpacker->setTarStatusProgressCallback(BaseUnpacker::defaultTarStatusProgressCallback );// print the filenames as they're expandedTARUnpacker->setTarMessageCallback(BaseUnpacker::targzPrintLoggerCallback );// tar log verbosityif( !TARUnpacker->tarExpander(tarGzFS,"/tar_example.tar",tarGzFS,"/") ) {Serial.printf("tarExpander failed with return code #%d\n",TARUnpacker->tarGzGetError() ); }
// mount spiffs (or any other filesystem)tarGzFS.begin();TarGzUnpacker*TARGZUnpacker=newTarGzUnpacker();TARGZUnpacker->haltOnError( true );// stop on fail (manual restart/reset required)TARGZUnpacker->setTarVerify( true );// true = enables health checks but slows down the overall processTARGZUnpacker->setupFSCallbacks(targzTotalBytesFn,targzFreeBytesFn );// prevent the partition from exploding, recommendedTARGZUnpacker->setGzProgressCallback(BaseUnpacker::defaultProgressCallback );// targzNullProgressCallback or defaultProgressCallbackTARGZUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback );// gz log verbosityTARGZUnpacker->setTarProgressCallback(BaseUnpacker::defaultProgressCallback );// prints the untarring progress for each individual fileTARGZUnpacker->setTarStatusProgressCallback(BaseUnpacker::defaultTarStatusProgressCallback );// print the filenames as they're expandedTARGZUnpacker->setTarMessageCallback(BaseUnpacker::targzPrintLoggerCallback );// tar log verbosity// using an intermediate file (default is /tmp/tmp.tar)if( !TARGZUnpacker->tarGzExpander(tarGzFS,"/targz_example.tar.gz",tarGzFS,"/tmp") ) {Serial.printf("tarGzExpander+intermediate file failed with return code #%d\n",TARGZUnpacker->tarGzGetError() ); }// or without intermediate fileif( !TARGZUnpacker->tarGzExpander(tarGzFS,"/targz_example.tar.gz",tarGzFS,"/tmp",nullptr ) ) {Serial.printf("tarGzExpander+intermediate file failed with return code #%d\n",TARGZUnpacker->tarGzGetError() ); }
// mount spiffs (or any other filesystem)tarGzFS.begin();GzUnpacker*GZUnpacker=newGzUnpacker();GZUnpacker->haltOnError( true );// stop on fail (manual restart/reset required)GZUnpacker->setupFSCallbacks(targzTotalBytesFn,targzFreeBytesFn );// prevent the partition from exploding, recommendedGZUnpacker->setGzProgressCallback(BaseUnpacker::defaultProgressCallback );// targzNullProgressCallback or defaultProgressCallbackGZUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback );// gz log verbosityif( !GZUnpacker->gzUpdater(tarGzFS,firmwareFile,U_FLASH,/*don't restart after update*/false ) ) {Serial.printf("gzUpdater failed with return code #%d\n",GZUnpacker->tarGzGetError() ); }
// mount spiffs (or any other filesystem)tarGzFS.begin();fs::Filefile=tarGzFS.open("/example_firmware.gz","r" );if (!file) {Serial.println("Can't open file");return; }GzUnpacker*GZUnpacker=newGzUnpacker();GZUnpacker->haltOnError( true );// stop on fail (manual restart/reset required)GZUnpacker->setupFSCallbacks(targzTotalBytesFn,targzFreeBytesFn );// prevent the partition from exploding, recommendedGZUnpacker->setGzProgressCallback(BaseUnpacker::defaultProgressCallback );// targzNullProgressCallback or defaultProgressCallbackGZUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback );// gz log verbosityif( !GZUnpacker->gzStreamUpdater( (Stream*)&file,UPDATE_SIZE_UNKNOWN ) ) {Serial.printf("gzStreamUpdater failed with return code #%d\n",GZUnpacker->tarGzGetError() ); }
// mount spiffs (or any other filesystem)tarGzFS.begin();fs::Filefile=tarGzFS.open("/example_archive.tgz","r" );if (!file) {Serial.println("Can't open file");return; }TarGzUnpacker*TARGZUnpacker=newTarGzUnpacker();TARGZUnpacker->haltOnError( true );// stop on fail (manual restart/reset required)TARGZUnpacker->setTarVerify( true );// true = enables health checks but slows down the overall processTARGZUnpacker->setupFSCallbacks(targzTotalBytesFn,targzFreeBytesFn );// prevent the partition from exploding, recommendedTARGZUnpacker->setGzProgressCallback(BaseUnpacker::defaultProgressCallback );// targzNullProgressCallback or defaultProgressCallbackTARGZUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback );// gz log verbosityTARGZUnpacker->setTarProgressCallback(BaseUnpacker::defaultProgressCallback );// prints the untarring progress for each individual fileTARGZUnpacker->setTarStatusProgressCallback(BaseUnpacker::defaultTarStatusProgressCallback );// print the filenames as they're expandedTARGZUnpacker->setTarMessageCallback(BaseUnpacker::targzPrintLoggerCallback );// tar log verbosityif( !TARGZUnpacker->tarGzStreamExpander( (Stream*)&file,tarGzFS ) ) {Serial.printf("tarGzStreamExpander failed with return code #%d\n",TARGZUnpacker->tarGzGetError() ); }
TarGzUnpacker*TARGZUnpacker=newTarGzUnpacker();TARGZUnpacker->haltOnError( true );// stop on fail (manual restart/reset required)TARGZUnpacker->setTarVerify( false );// nothing to verify as we're writing a partitionTARGZUnpacker->setGzProgressCallback(BaseUnpacker::targzNullProgressCallback );// don't care about gz progressTARGZUnpacker->setTarProgressCallback(BaseUnpacker::defaultProgressCallback );// prints the untarring progress for each individual partitionTARGZUnpacker->setTarStatusProgressCallback(BaseUnpacker::defaultTarStatusProgressCallback );// print the filenames as they're expandedTARGZUnpacker->setTarMessageCallback(myTarMessageCallback/*BaseUnpacker::targzPrintLoggerCallback*/ );// tar log verbosity// mount SDSD.begin();// this .tar.gz file has both the "app.ino.bin" and "app.spiffs.bin" partitionsfs::Filefile=SD.open("/bundle_firmware.tar.gz","r" );if (!file) {Serial.println("Can't open file");return; }// this could also be a HTTP/HTTPS/UDP/Ethernet StreamStream*streamptr=&file;if( !TARGZUnpacker->tarGzStreamUpdater(streamptr ) ) {Serial.printf("tarGzStreamUpdater failed with return code #%d\n",TARGZUnpacker->tarGzGetError() ); }else {Serial.println("Flashing successful, now restarting" );ESP.restart(); }
// buffer to stream (best compression)size_tcompress(uint8_t* srcBuf,size_t srcBufLen, Stream* dstStream );// buffer to buffer (best compression)size_tcompress(uint8_t* srcBuf,size_t srcBufLen,uint8_t** dstBufPtr );// stream to buffersize_tcompress( Stream* srcStream,size_t srcLen,uint8_t** dstBufPtr );// stream to streamsize_tcompress( Stream* srcStream,size_t srcLen, Stream* dstStream );// stream to filesize_tcompress( Stream* srcStream,size_t srcLen, fs::FS*dstFS,constchar* dstFilename );// file to filesize_tcompress( fs::FS *srcFS,constchar* srcFilename, fs::FS*dstFS,constchar* dstFilename );// file to streamsize_tcompress( fs::FS *srcFS,constchar* srcFilename, Stream* dstStream );
constchar*json="{\"hello\":\"world\"}";// input bufferFileout=LittleFS.open("/out.gz","w");// output streamsize_tcompressedSize=LZPacker::compress( (uint8_t*)json,strlen(json),&out );out.close();
constchar*json="{\"hello\":\"world\"}";// input bufferuint8_t*compressedBytes;// output buffersize_tcompressedSize=LZPacker::compress( (uint8_t*)json,strlen(json),&compressedBytes);// do something with compressedBytesfree(compressedBytes);
Filein=LittleFS.open("/my.uncompressed.file.txt");// input streamuint8_t*compressedBytes;// output buffersize_tcompressedSize=LZPacker::compress(&in,in.size(),&compressedBytes );// do something with compressedBytesfree(compressedBytes);in.close();
Filein=LittleFS.open("/my.uncompressed.file.txt");// input streamFileout=LittleFS.open("/out.gz","w");// output streamsize_tcompressedSize=LZPacker::compress(&in,in.size(),&out );out.close();in.close();
intpack_files(fs::FS *srcFS, std::vector<dir_entity_t> dirEntities, Stream* dstStream,constchar* tar_prefix=nullptr);intpack_files(fs::FS *srcFS, std::vector<dir_entity_t> dirEntities, fs::FS *dstFS,constchar*tar_output_file_path,constchar* tar_prefix=nullptr);
std::vector<TAR::dir_entity_t>dirEntities;TarPacker::collectDirEntities(&dirEntities,&LittleFS,"/folder/to/pack"); autopackedSize=TarPacker::pack_files(&LittleFS,dirEntities,&LittleFS, "/my.archive.tar");
std::vector<TAR::dir_entity_t>dirEntities;TarPacker::collectDirEntities(&dirEntities,&LittleFS,"/folder/to/pack");FiletarOutfile=LittleFS.open("/my.archive.tar","w");size_tpackedSize=TarPacker::pack_files(&LittleFS,dirEntities,&tarOutfile);tarOutfile.close();
intcompress(fs::FS *srcFS,constchar* srcDir, Stream* dstStream,constchar* tar_prefix=nullptr);intcompress(fs::FS *srcFS,constchar* srcDir, fs::FS *dstFS,constchar* tgz_name,constchar* tar_prefix=nullptr);intcompress(fs::FS *srcFS, std::vector<dir_entity_t> dirEntities, Stream* dstStream,constchar* tar_prefix=nullptr);intcompress(fs::FS *srcFS, std::vector<dir_entity_t> dirEntities, fs::FS *dstFS,constchar* tgz_name,constchar* tar_prefix=nullptr);
Pack & compress to.tar.gz
file/stream (no filtering on source files/folders list, recursion applies)
FileTarGzOutFile=LittleFS.open("/my.archive.tar.gz","w");size_tcompressedSize=TarGzPacker::compress(&LittleFS/*source*/,"/folder/to/compress",&TarGzOutFile);TarGzOutFile.close();
std::vector<TAR::dir_entity_t>dirEntities;TarPacker::collectDirEntities(&dirEntities,&LittleFS/*source*/,"/folder/to/compress");// eventually filter content from dirEntitiesFileTarGzOutFile=LittleFS.open("/my.archive.tar.gz","w");size_tcompressedSize=TarGzPacker::compress(&LittleFS/*source*/,dirEntities,&TarGzOutFile);TarGzOutFile.close();
FileTarGzOutFile=LittleFS.open("/my.archive.tar.gz","w");size_tcompressedSize=TarGzPacker::compress(&LittleFS/*source*/,"/folder/to/compress",&LittleFS/*destination*/,"/my.archive.tar.gz");TarGzOutFile.close();
std::vector<TAR::dir_entity_t>dirEntities;TarPacker::collectDirEntities(&dirEntities,&LittleFS/*source*/,"/folder/to/compress");// eventually filter content from dirEntitiesFileTarGzOutFile=LittleFS.open("/my.archive.tar.gz","w");size_tcompressedSize=TarGzPacker::compress(&LittleFS/*source*/,dirEntities,&LittleFS/*destination*/,"/my.archive.tar.gz");TarGzOutFile.close();
// basic progress callback (valid for tar or gzip)voidmyBasicProgressCallback(uint8_tprogress ) {Serial.printf("Progress: %d\n",progress ); }// complex progress callback (valid for tar or gzip)voidmyProgressCallback(uint8_tprogress ) {staticint8_tmyLastProgress=-1;if(myLastProgress!=progress ) {if(myLastProgress==-1 ) {Serial.print("Progress: "); }myLastProgress=progress;switch(progress ) {case0:Serial.print("0% ▓");break;case25:Serial.print(" 25% ");break;case50:Serial.print(" 50% ");break;case75:Serial.print(" 75% ");break;case100:Serial.print("▓ 100%\n");myLastProgress=-1;break;default:if(progress<100)Serial.print("▓" );break; } } }// General Error/Warning/Info loggervoidmyLogger(constchar*format, ...) {va_listargs;va_start(args,format);vprintf(format,args);va_end(args); }// status callback for TAR (fired at file creation)voidmyTarStatusProgressCallback(constchar*name,size_tsize,size_ttotal_unpacked ) {Serial.printf("[TAR] %-64s %8d bytes - %8d Total bytes\n",name,size,total_unpacked ); }
*Unpacker->tarGzGetError()
returns a value when a problem occured:
General library error codes
0
: Yay no error!-1
: Filesystem error-6
: Same a Filesystem error-7
: Update not finished? Something went wrong-38
: Logic error during deflating-39
: Logic error during gzip read-40
: Logic error during file creation-100
: No space left on device-101
: No space left on device-102
: No space left on device-103
: Not enough heap-104
: Gzip dictionnary needs to be enabled-105
: Gz Error when parsing header-106
: Gz Error when allocating memory-107
: General error, file integrity check fail
UZLIB: forwarding error values from uzlib.h as is (no offset)
-2
: Not a valid gzip file-3
: Gz Error TINF_DATA_ERROR-4
: Gz Error TINF_CHKSUM_ERROR-5
: Gz Error TINF_DICT_ERROR-41
: Gz error, can't guess filename
UPDATE: applying -20 offset to forwarded error values from Update.h
-8
: Updater Error UPDATE_ERROR_ABORT-9
: Updater Error UPDATE_ERROR_BAD_ARGUMENT-10
: Updater Error UPDATE_ERROR_NO_PARTITION-11
: Updater Error UPDATE_ERROR_ACTIVATE-12
: Updater Error UPDATE_ERROR_MAGIC_BYTE-13
: Updater Error UPDATE_ERROR_MD5-14
: Updater Error UPDATE_ERROR_STREAM-15
: Updater Error UPDATE_ERROR_SIZE-16
: Updater Error UPDATE_ERROR_SPACE-17
: Updater Error UPDATE_ERROR_READ-18
: Updater Error UPDATE_ERROR_ERASE-19
: Updater Error UPDATE_ERROR_WRITE
TAR: applying -30 offset to forwarded error values from untar.h
32
: Tar Error TAR_ERR_DATACB_FAIL33
: Tar Error TAR_ERR_HEADERCB_FAIL34
: Tar Error TAR_ERR_FOOTERCB_FAIL35
: Tar Error TAR_ERR_READBLOCK_FAIL36
: Tar Error TAR_ERR_HEADERTRANS_FAIL37
: Tar Error TAR_ERR_HEADERPARSE_FAIL38
: Tar Error TAR_ERROR_HEAP
- SPIFFS is deprecated, migrate to LittleFS!
- tarGzExpander/tarExpander: symlinks or long filename/path not supported, path limit is 100 chars
- tarGzExpander without intermediate file uses a lot of heap
- 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
- ESP32: use all of the "Debug level" values from the boards menu
- ESP8266: Warning/Error when "Debug Port:Serial" is used, and Debug/Verbose when "Debug Level:Core" is selected from the boards menu
- RP2040: only "Debug port: Serial" and "Debug Level: Core" enable logging
- ESP8266 Sketch Data Upload tool for LittleFS
- ESP32 Sketch Data Upload tool for FFat/LittleFS/SPIFFS
- Pico LittlsFS Data Upload tool
- https://github.com/vortigont/esp32-flashz OTA-Update your ESP32 from zlib compressed binaries (not gzip)
- https://github.com/chrisjoyce911/esp32FOTA OTA-Update your ESP32 from zlib or gzip compressed binaries
- https://github.com/laukik-hase/esp_compression inflate/deflate miniz/uzlib based esp-idf implementation
- pfalcon (uzlib maintainer)
- dsoprea (TinyUntar maintainer)
- lorol (LittleFS-ESP32 + fs plugin)
- me-no-dev (inspiration and support)
- atanisoft (motivation and support)
- lbernstone (motivation and support)
- scubachristopher (contribution and support)
- infrafast (feedback fueler)
- vortigont (inspiration and support)
- hitecSmartHome (feedback fueler)
About
🗜️ An Arduino library to compress/decompress tar, gz, and tar.gz files on ESP32, ESP8266 and RP2040
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Contributors5
Uh oh!
There was an error while loading.Please reload this page.