tar-stream

repository·master·Indexed 19 days ago

https://github.com/mafintosh/tar-stream

A streaming tar parser and generator for Node.js that operates purely using streams, allowing for the extraction and creation of tarballs without requiring direct file system access. Version 3.2.0 provides high-performance processing via tar.pack() for generating archives and tar.extract() for parsing them, including support for the async iterator protocol.

Tokens
2.4K
Snippets
8
Records
9
Agent score
16%

What's inside tar-stream

  1. How tar-stream works

    master

    tar-stream is a streaming tar parser and generator. It operates purely using streams, allowing you to extract or parse tarballs without interacting with the file system.

    Note: If you are working with compressed files (e.g., .tar.gz), you must decompress them first. It is recommended to use gunzip-maybe in conjunction with tar-stream.

  2. Save a tarball to the file system

    master

    To save a generated tarball to a file, create a write stream using fs.createWriteStream() and pipe the pack stream into it.

    const fs = require('fs')
    const tar = require('tar-stream')
    
    const pack = tar.pack() // pack is a stream
    const path = 'YourTarBall.tar'
    const yourTarball = fs.createWriteStream(path)
    
    // add a file called YourFile.txt with the content "Hello World!"
    pack.entry({ name: 'YourFile.txt' }, 'Hello World!', function (err) {
      if (err) throw err
      pack.finalize()
    })
    
    // pipe the pack stream to your file
    pack.pipe(yourTarball)
    
    yourTarball.on('close', function () {
      console.log(path + ' has been written')
      fs.stat(path, function(err, stats) {
        if (err) throw err
        console.log(stats)
        console.log('Got file info successfully!')
      })
    })
  3. Modify an existing tarball

    master

    You can rewrite paths, change modes, or modify content in an existing tarball by piping an extract stream into a pack stream. For each entry in the extractor, modify the header object and pipe the entry's content stream into pack.entry(header, callback).

    const extract = tar.extract()
    const pack = tar.pack()
    const path = require('path')
    
    extract.on('entry', function (header, stream, callback) {
      // let's prefix all names with 'tmp'
      header.name = path.join('tmp', header.name)
      // write the new entry to the pack stream
      stream.pipe(pack.entry(header, callback))
    })
    
    extract.on('finish', function () {
      // all entries done - lets finalize it
      pack.finalize()
    })
    
    // pipe the old tarball to the extractor
    oldTarballStream.pipe(extract)
    
    // pipe the new tarball to another stream
    pack.pipe(newTarballStream)
  4. Extract tarballs with tar.extract()

    master

    To extract a tarball, use tar.extract(). You listen for the 'entry' event to process each file in the archive.

    Critical Requirement: The tar archive is streamed sequentially. You must drain each entry's stream (e.g., by calling stream.resume() or piping it) and call the next() callback when finished with an entry. Failure to do so will cause backpressure and stop the extraction process.

    const extract = tar.extract()
    
    extract.on('entry', function (header, stream, next) {
      // header is the tar header
      // stream is the content body (might be an empty stream)
      // call next when you are done with this entry
    
      stream.on('end', function () {
        next() // ready for next entry
      })
    
      stream.resume() // just auto drain the stream
    })
    
    extract.on('finish', function () {
      // all entries read
    })
    
    // Assuming 'pack' is a source stream
    pack.pipe(extract)
  5. Create tarballs with tar.pack()

    master

    To create a tarball, use tar.pack() to create a pack stream. You add entries to the archive using pack.entry(header, [callback]).

    • You can pass a string or buffer as the second argument for simple content.
    • You can pass a stream as the second argument for larger content.
    • Always call pack.finalize() when you have no more entries to add.
    const tar = require('tar-stream')
    const pack = tar.pack() // pack is a stream
    
    // add a file called my-test.txt with the content "Hello World!"
    pack.entry({ name: 'my-test.txt' }, 'Hello World!')
    
    // add a file called my-stream-test.txt from a stream
    const entry = pack.entry({ name: 'my-stream-test.txt', size: 11 }, function(err) {
      // the stream was added
      // no more entries
      pack.finalize()
    })
    
    entry.write('hello')
    entry.write(' ')
    entry.write('world')
    entry.end()
    
    // pipe the pack stream somewhere
    pack.pipe(process.stdout)
  6. Extract tarballs using an async iterator

    master

    The extraction stream provided by tar.extract() implements the async iterator protocol, allowing for a cleaner for await...of loop syntax.

    When using this pattern, you must call entry.resume() to drain the entry stream.

    const extract = tar.extract()
    
    someStream.pipe(extract)
    
    for await (const entry of extract) {
      entry.header // the tar header
      entry.resume() // the entry is the stream also
    }
  7. Reference the tar entry header properties

    master

    When using pack.entry(header) or receiving a header in extract.on('entry', ...), the header object can contain the following properties. Most values can be obtained via fs.stat().

    {
      name: 'path/to/this/entry.txt',
      size: 1314,        // entry size. defaults to 0
      mode: 0o644,       // entry mode. defaults to 0o755 for dirs and 0o644 otherwise
      mtime: new Date(), // last modified date for entry. defaults to now.
      type: 'file',      // type of entry. defaults to file. can be:
                         // file | link | symlink | directory | block-device
                         // character-device | fifo | contiguous-file
      linkname: 'path',  // linked file name
      uid: 0,            // uid of entry owner. defaults to 0
      gid: 0,            // gid of entry owner. defaults to 0
      uname: 'maf',      // uname of entry owner. defaults to null
      gname: 'staff',    // gname of entry owner. defaults to null
      devmajor: 0,       // device major version. defaults to 0
      devminor: 0        // device minor version. defaults to 0
    }
  8. Extract tarballs with tar.extract()

    master

    Use tar.extract() to parse a tar stream. This function returns a stream that emits entries from the tarball. Each entry is a stream that can be read to retrieve the file content.

    const tar = require('tar-stream')
    const extract = tar.extract()
    
    // Pipe a tar stream into extract
    tarStream.pipe(extract)
    
    extract.on('entry', (header, stream, next) => {
      // Handle the entry
      stream.on('data', (chunk) => {
        // Process chunk
      })
      stream.on('end', next)
    })