Add more complex writing example
diff --git a/.gitignore b/.gitignore
index e8735c4..15432f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,5 @@
# Generated in the example
test.tar
-.vscode
\ No newline at end of file
+self.tar.gz
+.vscode
diff --git a/README.md b/README.md
index 77410cd..99fa1be 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,13 @@
## Writing
-You can write tar files into a `StreamSink<List<int>>`, such as an `IOSink`:
+When writing archives, `package:tar` expects a `Stream` of tar entries to include in
+the archive.
+This stream can then be converted into a stream of byte-array chunks forming the
+encoded tar archive.
+
+To write a tar stream into a `StreamSink<List<int>>`, such as an `IOSink` returned by
+`File.openWrite`, use `tarWritingSink`:
```dart
import 'dart:convert';
@@ -63,8 +69,7 @@
Future<void> main() async {
final output = File('test.tar').openWrite();
-
- await Stream<TarEntry>.value(
+ final tarEntries = Stream<TarEntry>.value(
TarEntry.data(
TarHeader(
name: 'hello.txt',
@@ -72,11 +77,17 @@
),
utf8.encode('Hello world'),
),
- ).pipe(tarWritingSink(output));
+ );
+
+ await tarEntries.pipe(tarWritingSink(output));
}
```
-To write `.tar.gz` files, you can again transform the stream twice:
+For more complex stream transformations, `tarWriter` can be used as a stream
+transformer converting a stream of tar entries into archive bytes.
+
+Together with the `gzip.encoder` transformer from `dart:io`, this can be used
+to write a `.tar.gz` file:
```dart
import 'dart:io';
@@ -84,12 +95,14 @@
Future<void> write(Stream<TarEntry> entries) {
return entries
- .transform(tarWriter)
- .transform(gzip.encoder)
+ .transform(tarWriter) // convert entries into a .tar stream
+ .transform(gzip.encoder) // convert the .tar stream into a .tar.gz stream
.pipe(File('output.tar.gz').openWrite());
}
```
+A more complex example for writing files can be found in [`example/archive_self.dart`](example/archive_self.dart).
+
Note that, by default, tar files are written in the pax format defined by the
POSIX.1-2001 specification (`--format=posix` in GNU tar).
When all entries have file names shorter than 100 chars and a size smaller
@@ -101,14 +114,17 @@
```dart
Future<void> write(Stream<TarEntry> entries) {
return entries
- .transform(tarWriterWith(format: OutputFormat.gnuLongName))
- .pipe(tarWritingSink(
- File('output.tar.gz').openWrite(),
- format: OutputFormat.gnuLongName,
+ .pipe(
+ tarWritingSink(
+ File('output.tar').openWrite(),
+ format: OutputFormat.gnuLongName,
));
}
```
+To change the output format on the `tarWriter` transformer, use
+`tarWriterWith`.
+
## Features
- Supports v7, ustar, pax, gnu and star archives
@@ -119,4 +135,4 @@
-----
Big thanks to [Garett Tok Ern Liang](https://github.com/walnutdust) for writing the initial
-Dart tar reader that this library is based on.
\ No newline at end of file
+Dart tar reader that this library is based on.
diff --git a/example/archive_self.dart b/example/archive_self.dart
new file mode 100644
index 0000000..78ecbbb
--- /dev/null
+++ b/example/archive_self.dart
@@ -0,0 +1,59 @@
+import 'dart:io';
+
+import 'package:tar/tar.dart';
+import 'package:path/path.dart' as p;
+
+const outputName = 'self.tar.gz';
+
+/// This example creates a `.tar.gz` file of the directory its running in.
+Future<void> main() {
+ final entries = findEntries();
+ final output = File(outputName);
+
+ return entries
+ .transform(tarWriter)
+ .transform(gzip.encoder)
+ .pipe(output.openWrite());
+}
+
+Stream<TarEntry> findEntries() async* {
+ // Build a stream of tar entries by going through each file system entity in
+ // the current directory.
+ final root = Directory.current;
+ await for (final entry in root.list(recursive: true)) {
+ // We could write directories too, but we only care about files for
+ // simplicity.
+ if (entry is! File) continue;
+
+ final name = p.relative(entry.path, from: root.path);
+
+ // Let's also ignore hidden directories and files.
+ if (name.startsWith('.')) continue;
+
+ // Finally, we should ignore the output file since weird things may happen
+ // otherwise.
+ if (name == outputName) continue;
+
+ final stat = entry.statSync();
+
+ yield TarEntry(
+ TarHeader(
+ name: name,
+ typeFlag: TypeFlag.reg, // It's a regular file
+ // Apart from that, copy over meta information
+ mode: stat.mode,
+ modified: stat.modified,
+ accessed: stat.accessed,
+ changed: stat.changed,
+ // This assumes that the file won't change until we're writing it into
+ // the archive later, since then the size might be wrong. It's more
+ // efficient though, since the tar writer would otherwise have to buffer
+ // everything to find out the size.
+ size: stat.size,
+ ),
+ // Use entry.openRead() to obtain an input stream for the file that the
+ // writer will use later.
+ entry.openRead(),
+ );
+ }
+}