golang-github-jhoonb-archivex/0000755000175000017500000000000013261572156015361 5ustar aronarongolang-github-jhoonb-archivex/archivex_test.go0000644000175000017500000000620113261572136020555 0ustar aronaron////////////////////////////////////////// // archivex_test.go // Jhonathan Paulo Banczek - 2014 // jpbanczek@gmail.com - jhoonb.com ////////////////////////////////////////// package archivex import ( "fmt" "os" "path" "reflect" "testing" ) type archTest struct { addPath string include bool name string filePath string addString string addFileName string } type archTypeTest struct { tests []archTest arch Archivex } func Test_archivex(t *testing.T) { dir, _ := os.Getwd() // let's clean up the previous results, to be sure that we're not reading from an old result. if err := os.RemoveAll(path.Join(dir, "/testresults")); err != nil && !os.IsNotExist(err) { t.Fatalf("cannot clean up test results directory: %v", err) } if err := os.Mkdir(path.Join(dir, "/testresults"), 0777); err != nil && !os.IsExist(err) { t.Fatalf("cannot make test results directory: %v", err) } // All the different tests we want to run with different combinations of input paths and the includeCurrentFolder flag tests := []archTest{ // absolute path archTest{dir + "/testfolder/", true, "absTrailInclude", dir + "/LICENSE", "string", "filename"}, archTest{dir + "/testfolder/", false, "absTrailExclude", dir + "/LICENSE", "string", "filename"}, // relative path archTest{"testfolder/", true, "relTrailInclude", "LICENSE", "string", "filename"}, archTest{"testfolder/", false, "relTrailExclude", "LICENSE", "string", "filename"}, // without trailing slashes archTest{dir + "/testfolder", true, "absInclude", dir + "/LICENSE", "string", "filename"}, archTest{dir + "/testfolder", false, "absExclude", dir + "/LICENSE", "string", "filename"}, archTest{"testfolder", true, "relInclude", "LICENSE", "string", "filename"}, archTest{"testfolder", false, "relExclude", "LICENSE", "string", "filename"}, } // We want to execute the batch of tests on both Zip and Tar typeTests := []archTypeTest{ archTypeTest{tests, &ZipFile{}}, archTypeTest{tests, &TarFile{}}, } // Run all tests for _, typeTest := range typeTests { currentType := reflect.TypeOf(typeTest.arch) t.Logf("Running tests for archive type: %s", currentType.Elem()) for i, test := range typeTest.tests { t.Logf("Running %s...", test.name) // Create the archive filename := fmt.Sprintf("%d_%s_test", i+1, test.name) arch := reflect.ValueOf(typeTest.arch).Interface().(Archivex) if err := arch.Create(path.Join("testresults", filename)); err != nil { t.Fatalf("Error creating '%s': %v", filename, err) } // Add the files to the archive if err := arch.AddAll(test.addPath, test.include); err != nil { t.Fatalf("Error doing AddAll with '%s' and includeCurrentFolder = %v: %v", test.addPath, test.include, err) } // Add a file to the archive if err := arch.AddFile(test.filePath); err != nil { t.Fatalf("Error doing AddFile with '%s': %v", test.filePath, err) } //} // Add a file to the archive if err := arch.Add(test.addFileName, []byte(test.addString)); err != nil { t.Fatalf("Error doing Add with '%s', '%s': %v", test.addString, test.addFileName, err) } // Close the archive arch.Close() } } } golang-github-jhoonb-archivex/testfolder/0000755000175000017500000000000013261572136017532 5ustar aronarongolang-github-jhoonb-archivex/testfolder/test1.txt0000644000175000017500000000010213261572136021324 0ustar aronarontest package archivex jhoonb.com github.com/jhoonb/archivex ;)golang-github-jhoonb-archivex/testfolder/1/0000755000175000017500000000000013261572136017672 5ustar aronarongolang-github-jhoonb-archivex/testfolder/1/bla.txt0000644000175000017500000000001113261572136021161 0ustar aronaronasdfasdfsgolang-github-jhoonb-archivex/testfolder/test2.txt0000644000175000017500000000010213261572136021325 0ustar aronarontest package archivex jhoonb.com github.com/jhoonb/archivex ;)golang-github-jhoonb-archivex/LICENSE0000644000175000017500000000272213261572136016367 0ustar aronaronCopyright (c) 2014, Jhonathan Paulo Banczek All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of archivex nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. golang-github-jhoonb-archivex/README.md0000644000175000017500000000250513261572136016640 0ustar aronaronarchivex ======== archivex is a golang package that archives folders (recursively) and files to zip and tar formats. [![Build Status](https://travis-ci.org/jhoonb/archivex.svg)](https://travis-ci.org/jhoonb/archivex) [![](http://gocover.io/_badge/github.com/jhoonb/archivex)](http://gocover.io/github.com/jhoonb/archivex) Installation ------------- ``` bash $ go get github.com/jhoonb/archivex ``` Example ------------- ```go package main import ( "github.com/jhoonb/archivex" ) // Example using only func zip func zip() { zip := new(archivex.ZipFile) zip.Create("filezip") zip.Add("testadd.txt", []byte("test 1")) zip.AddFile("") zip.AddAll("") tar.AddAll("") arch.AddAll("= blockSize { zippedFile.Write(bytes) continue } zippedFile.Write(bytes[:readedBytes]) } return nil } //AddFileWithName add a file to zip with a name func (z *ZipFile) AddFileWithName(name string, filepath string) error { zippedFile, err := z.createWriter(name) if err != nil { return err } file, e := os.Open(filepath) defer file.Close() if e != nil { return e } fileReader := bufio.NewReader(file) blockSize := 512 * 1024 // 512kb bytes := make([]byte, blockSize) for { readedBytes, err := fileReader.Read(bytes) if err != nil { if err.Error() == "EOF" { break } if err.Error() != "EOF" { return err } } if readedBytes >= blockSize { zippedFile.Write(bytes) continue } zippedFile.Write(bytes[:readedBytes]) } return nil } // AddAll adds all files from dir in archive, recursively. // Directories receive a zero-size entry in the archive, with a trailing slash in the header name, and no compression func (z *ZipFile) AddAll(dir string, includeCurrentFolder bool) error { dir = path.Clean(dir) return addAll(dir, dir, includeCurrentFolder, func(info os.FileInfo, file io.Reader, entryName string) (err error) { // Create a header based off of the fileinfo header, err := zip.FileInfoHeader(info) if err != nil { return err } // If it's a file, set the compression method to deflate (leave directories uncompressed) if !info.IsDir() { header.Method = zip.Deflate } // Set the header's name to what we want--it may not include the top folder header.Name = entryName // Add a trailing slash if the entry is a directory if info.IsDir() { header.Name += "/" } // Get a writer in the archive based on our header writer, err := z.Writer.CreateHeader(header) if err != nil { return err } // If we have a file to write (i.e., not a directory) then pipe the file into the archive writer if file != nil { if _, err := io.Copy(writer, file); err != nil { return err } } return nil }) } //Close close the zip file func (z *ZipFile) Close() error { err := z.Writer.Close() z.file.Close() return err } // Create new Tar file func (t *TarFile) Create(name string) error { // check the filename extension // if it has a .gz, we'll compress it. if strings.HasSuffix(name, ".tar.gz") { t.Compressed = true } else { t.Compressed = false } // check to see if they have the wrong extension if strings.HasSuffix(name, ".tar.gz") != true && strings.HasSuffix(name, ".tar") != true { // is it .zip? replace it if strings.HasSuffix(name, ".zip") == true { name = strings.Replace(name, ".zip", ".tar.gz", -1) t.Compressed = true } else { // if it's not, add .tar // since we'll assume it's not compressed name = name + ".tar" } } t.Name = name file, err := os.Create(t.Name) if err != nil { return err } if t.Compressed { t.GzWriter = gzip.NewWriter(file) t.Writer = tar.NewWriter(t.GzWriter) } else { t.Writer = tar.NewWriter(file) } return nil } // Add add byte in archive tar func (t *TarFile) Add(name string, file []byte) error { hdr := &tar.Header{ Name: name, Size: int64(len(file)), Mode: 0666, ModTime: time.Now(), } if err := t.Writer.WriteHeader(hdr); err != nil { return err } _, err := t.Writer.Write(file) return err } // Add add byte in archive tar func (t *TarFile) AddWithHeader(name string, file []byte, hdr *tar.Header) error { if err := t.Writer.WriteHeader(hdr); err != nil { return err } _, err := t.Writer.Write(file) return err } // AddFile add file from dir in archive tar func (t *TarFile) AddFile(name string) error { bytearq, err := ioutil.ReadFile(name) if err != nil { return err } info, err := os.Stat(name) if err != nil { return err } header, err := tar.FileInfoHeader(info, "") if err != nil { return err } err = t.Writer.WriteHeader(header) if err != nil { return err } _, err = t.Writer.Write(bytearq) if err != nil { return err } return nil } // AddFile add file from dir in archive tar func (t *TarFile) AddFileWithName(name string, filename string) error { bytearq, err := ioutil.ReadFile(name) if err != nil { return err } info, err := os.Stat(name) if err != nil { return err } header, err := tar.FileInfoHeader(info, "") if err != nil { return err } header.Name = filename err = t.Writer.WriteHeader(header) if err != nil { return err } _, err = t.Writer.Write(bytearq) if err != nil { return err } return nil } // AddAll adds all files from dir in archive // Tar does not support directories func (t *TarFile) AddAll(dir string, includeCurrentFolder bool) error { dir = path.Clean(dir) return addAll(dir, dir, includeCurrentFolder, func(info os.FileInfo, file io.Reader, entryName string) (err error) { // Create a header based off of the fileinfo header, err := tar.FileInfoHeader(info, "") if err != nil { return err } // Set the header's name to what we want--it may not include the top folder header.Name = entryName // Write the header into the tar file if err := t.Writer.WriteHeader(header); err != nil { return err } // The directory don't need copy file if file == nil { return nil } // Pipe the file into the tar if _, err := io.Copy(t.Writer, file); err != nil { return err } return nil }) } // Close the file Tar func (t *TarFile) Close() error { err := t.Writer.Close() if err != nil { return err } if t.Compressed { err = t.GzWriter.Close() if err != nil { return err } } return err } func getSubDir(dir string, rootDir string, includeCurrentFolder bool) (subDir string) { subDir = strings.Replace(dir, rootDir, "", 1) // Remove leading slashes, since this is intentionally a subdirectory. if len(subDir) > 0 && subDir[0] == os.PathSeparator { subDir = subDir[1:] } subDir = path.Join(strings.Split(subDir, string(os.PathSeparator))...) if includeCurrentFolder { parts := strings.Split(rootDir, string(os.PathSeparator)) subDir = path.Join(parts[len(parts)-1], subDir) } return } // addAll is used to recursively go down through directories and add each file and directory to an archive, based on an ArchiveWriteFunc given to it func addAll(dir string, rootDir string, includeCurrentFolder bool, writerFunc ArchiveWriteFunc) error { // Get a list of all entries in the directory, as []os.FileInfo fileInfos, err := ioutil.ReadDir(dir) if err != nil { return err } // Loop through all entries for _, info := range fileInfos { full := filepath.Join(dir, info.Name()) // If the entry is a file, get an io.Reader for it var file *os.File var reader io.Reader if !info.IsDir() { file, err = os.Open(full) if err != nil { return err } reader = file } // Write the entry into the archive subDir := getSubDir(dir, rootDir, includeCurrentFolder) entryName := path.Join(subDir, info.Name()) if err := writerFunc(info, reader, entryName); err != nil { if file != nil { file.Close() } return err } if file != nil { if err := file.Close(); err != nil { return err } } // If the entry is a directory, recurse into it if info.IsDir() { addAll(full, rootDir, includeCurrentFolder, writerFunc) } } return nil } golang-github-jhoonb-archivex/.travis.yml0000644000175000017500000000012513261572136017466 0ustar aronaronlanguage: go go: - 1.4 - tip install: go get -v ./... script: go test -v ./...