github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/src/archive/tar/example_test.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package tar_test
     6  
     7  import (
     8  	"archive/tar"
     9  	"bytes"
    10  	"crypto/md5"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"log"
    15  	"os"
    16  	"strings"
    17  )
    18  
    19  func Example_minimal() {
    20  	// Create and add some files to the archive.
    21  	var buf bytes.Buffer
    22  	tw := tar.NewWriter(&buf)
    23  	var files = []struct {
    24  		Name, Body string
    25  	}{
    26  		{"readme.txt", "This archive contains some text files."},
    27  		{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
    28  		{"todo.txt", "Get animal handling license."},
    29  	}
    30  	for _, file := range files {
    31  		hdr := &tar.Header{
    32  			Name: file.Name,
    33  			Mode: 0600,
    34  			Size: int64(len(file.Body)),
    35  		}
    36  		if err := tw.WriteHeader(hdr); err != nil {
    37  			log.Fatal(err)
    38  		}
    39  		if _, err := tw.Write([]byte(file.Body)); err != nil {
    40  			log.Fatal(err)
    41  		}
    42  	}
    43  	if err := tw.Close(); err != nil {
    44  		log.Fatal(err)
    45  	}
    46  
    47  	// Open and iterate through the files in the archive.
    48  	tr := tar.NewReader(&buf)
    49  	for {
    50  		hdr, err := tr.Next()
    51  		if err == io.EOF {
    52  			break // End of archive
    53  		}
    54  		if err != nil {
    55  			log.Fatal(err)
    56  		}
    57  		fmt.Printf("Contents of %s:\n", hdr.Name)
    58  		if _, err := io.Copy(os.Stdout, tr); err != nil {
    59  			log.Fatal(err)
    60  		}
    61  		fmt.Println()
    62  	}
    63  
    64  	// Output:
    65  	// Contents of readme.txt:
    66  	// This archive contains some text files.
    67  	// Contents of gopher.txt:
    68  	// Gopher names:
    69  	// George
    70  	// Geoffrey
    71  	// Gonzo
    72  	// Contents of todo.txt:
    73  	// Get animal handling license.
    74  }
    75  
    76  // A sparse file can efficiently represent a large file that is mostly empty.
    77  // When packing an archive, Header.DetectSparseHoles can be used to populate
    78  // the sparse map, while Header.PunchSparseHoles can be used to create a
    79  // sparse file on disk when extracting an archive.
    80  func Example_sparseAutomatic() {
    81  	// Create the source sparse file.
    82  	src, err := ioutil.TempFile("", "sparse.db")
    83  	if err != nil {
    84  		log.Fatal(err)
    85  	}
    86  	defer os.Remove(src.Name()) // Best-effort cleanup
    87  	defer func() {
    88  		if err := src.Close(); err != nil {
    89  			log.Fatal(err)
    90  		}
    91  	}()
    92  	if err := src.Truncate(10e6); err != nil {
    93  		log.Fatal(err)
    94  	}
    95  	for i := 0; i < 10; i++ {
    96  		if _, err := src.Seek(1e6-1e3, io.SeekCurrent); err != nil {
    97  			log.Fatal(err)
    98  		}
    99  		if _, err := src.Write(bytes.Repeat([]byte{'0' + byte(i)}, 1e3)); err != nil {
   100  			log.Fatal(err)
   101  		}
   102  	}
   103  
   104  	// Create an archive and pack the source sparse file to it.
   105  	var buf bytes.Buffer
   106  	tw := tar.NewWriter(&buf)
   107  	fi, err := src.Stat()
   108  	if err != nil {
   109  		log.Fatal(err)
   110  	}
   111  	hdr, err := tar.FileInfoHeader(fi, "")
   112  	if err != nil {
   113  		log.Fatal(err)
   114  	}
   115  	if err := hdr.DetectSparseHoles(src); err != nil {
   116  		log.Fatal(err)
   117  	}
   118  	if err := tw.WriteHeader(hdr); err != nil {
   119  		log.Fatal(err)
   120  	}
   121  	if _, err := io.Copy(tw, src); err != nil {
   122  		log.Fatal(err)
   123  	}
   124  	if err := tw.Close(); err != nil {
   125  		log.Fatal(err)
   126  	}
   127  
   128  	// Create the destination sparse file.
   129  	dst, err := ioutil.TempFile("", "sparse.db")
   130  	if err != nil {
   131  		log.Fatal(err)
   132  	}
   133  	defer os.Remove(dst.Name()) // Best-effort cleanup
   134  	defer func() {
   135  		if err := dst.Close(); err != nil {
   136  			log.Fatal(err)
   137  		}
   138  	}()
   139  
   140  	// Open the archive and extract the sparse file into the destination file.
   141  	tr := tar.NewReader(&buf)
   142  	hdr, err = tr.Next()
   143  	if err != nil {
   144  		log.Fatal(err)
   145  	}
   146  	if err := hdr.PunchSparseHoles(dst); err != nil {
   147  		log.Fatal(err)
   148  	}
   149  	if _, err := io.Copy(dst, tr); err != nil {
   150  		log.Fatal(err)
   151  	}
   152  
   153  	// Verify that the sparse files are identical.
   154  	want, err := ioutil.ReadFile(src.Name())
   155  	if err != nil {
   156  		log.Fatal(err)
   157  	}
   158  	got, err := ioutil.ReadFile(dst.Name())
   159  	if err != nil {
   160  		log.Fatal(err)
   161  	}
   162  	fmt.Printf("Src MD5: %08x\n", md5.Sum(want))
   163  	fmt.Printf("Dst MD5: %08x\n", md5.Sum(got))
   164  
   165  	// Output:
   166  	// Src MD5: 33820d648d42cb3da2515da229149f74
   167  	// Dst MD5: 33820d648d42cb3da2515da229149f74
   168  }
   169  
   170  // The SparseHoles can be manually constructed without Header.DetectSparseHoles.
   171  func Example_sparseManual() {
   172  	// Define a sparse file to add to the archive.
   173  	// This sparse files contains 5 data fragments, and 4 hole fragments.
   174  	// The logical size of the file is 16 KiB, while the physical size of the
   175  	// file is only 3 KiB (not counting the header data).
   176  	hdr := &tar.Header{
   177  		Name: "sparse.db",
   178  		Size: 16384,
   179  		SparseHoles: []tar.SparseEntry{
   180  			// Data fragment at 0..1023
   181  			{Offset: 1024, Length: 1024 - 512}, // Hole fragment at 1024..1535
   182  			// Data fragment at 1536..2047
   183  			{Offset: 2048, Length: 2048 - 512}, // Hole fragment at 2048..3583
   184  			// Data fragment at 3584..4095
   185  			{Offset: 4096, Length: 4096 - 512}, // Hole fragment at 4096..7679
   186  			// Data fragment at 7680..8191
   187  			{Offset: 8192, Length: 8192 - 512}, // Hole fragment at 8192..15871
   188  			// Data fragment at 15872..16383
   189  		},
   190  	}
   191  
   192  	// The regions marked as a sparse hole are filled with NUL-bytes.
   193  	// The total length of the body content must match the specified Size field.
   194  	body := "" +
   195  		strings.Repeat("A", 1024) +
   196  		strings.Repeat("\x00", 1024-512) +
   197  		strings.Repeat("B", 512) +
   198  		strings.Repeat("\x00", 2048-512) +
   199  		strings.Repeat("C", 512) +
   200  		strings.Repeat("\x00", 4096-512) +
   201  		strings.Repeat("D", 512) +
   202  		strings.Repeat("\x00", 8192-512) +
   203  		strings.Repeat("E", 512)
   204  
   205  	h := md5.Sum([]byte(body))
   206  	fmt.Printf("Write content of %s, Size: %d, MD5: %08x\n", hdr.Name, len(body), h)
   207  	fmt.Printf("Write SparseHoles of %s:\n\t%v\n\n", hdr.Name, hdr.SparseHoles)
   208  
   209  	// Create a new archive and write the sparse file.
   210  	var buf bytes.Buffer
   211  	tw := tar.NewWriter(&buf)
   212  	if err := tw.WriteHeader(hdr); err != nil {
   213  		log.Fatal(err)
   214  	}
   215  	if _, err := tw.Write([]byte(body)); err != nil {
   216  		log.Fatal(err)
   217  	}
   218  	if err := tw.Close(); err != nil {
   219  		log.Fatal(err)
   220  	}
   221  
   222  	// Open and iterate through the files in the archive.
   223  	tr := tar.NewReader(&buf)
   224  	for {
   225  		hdr, err := tr.Next()
   226  		if err == io.EOF {
   227  			break
   228  		}
   229  		if err != nil {
   230  			log.Fatal(err)
   231  		}
   232  		body, err := ioutil.ReadAll(tr)
   233  		if err != nil {
   234  			log.Fatal(err)
   235  		}
   236  
   237  		h := md5.Sum([]byte(body))
   238  		fmt.Printf("Read content of %s, Size: %d, MD5: %08x\n", hdr.Name, len(body), h)
   239  		fmt.Printf("Read SparseHoles of %s:\n\t%v\n\n", hdr.Name, hdr.SparseHoles)
   240  	}
   241  
   242  	// Output:
   243  	// Write content of sparse.db, Size: 16384, MD5: 9b4e2cfae0f9303d30237718e891e9f9
   244  	// Write SparseHoles of sparse.db:
   245  	// 	[{1024 512} {2048 1536} {4096 3584} {8192 7680}]
   246  	//
   247  	// Read content of sparse.db, Size: 16384, MD5: 9b4e2cfae0f9303d30237718e891e9f9
   248  	// Read SparseHoles of sparse.db:
   249  	// 	[{1024 512} {2048 1536} {4096 3584} {8192 7680} {16384 0}]
   250  }