This commit is contained in:
yi-ge 2019-08-08 21:50:32 +08:00
parent 314f38c25e
commit dd0bac39d4
17 changed files with 463 additions and 2 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Mac
.DS_Store
# Test out
/test/out

23
.travis.yml Normal file
View File

@ -0,0 +1,23 @@
language: go
sudo: required
matrix:
allow_failures:
- go: master
include:
# Supported versions of Go: https://golang.org/dl/
- go: '1.11.x'
- go: '1.12.x'
- go: master
go_import_path: github.com/yi-ge/unzip
cache:
directories:
- $GOPATH/pkg
before_install:
- echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}"
script:
- go test -v

View File

@ -1,2 +1,21 @@
# unzip
Golang *.zip decompress.
# Unzip
Golang \*.zip decompress.
[![Build Status](https://img.shields.io/travis/com/yi-ge/unzip/master.svg)](https://travis-ci.com/yi-ge/unzip)
[![GoDoc](https://godoc.org/github.com/yi-ge/unzip?status.svg)](https://godoc.org/github.com/yi-ge/unzip)
Golang \*.tar.xz decompress.
Fork from [https://github.com/artdarek/go-unzip](https://github.com/artdarek/go-unzip) and remove print, add support for Symlink.
Thank artdarek.
## Usage
```golang
import "github.com/yi-ge/unzip"
u := unzip.New(filePath, outDir)
err := u.Extract()
```

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/yi-ge/unzip
go 1.12

0
test/a.txt Normal file
View File

1
test/b.txt Normal file
View File

@ -0,0 +1 @@
1

1
test/c.txt Normal file
View File

@ -0,0 +1 @@
2

1
test/d.txt Symbolic link
View File

@ -0,0 +1 @@
dir/d.txt

1
test/dir/c.txt Normal file
View File

@ -0,0 +1 @@
2

1
test/dir/d.txt Normal file
View File

@ -0,0 +1 @@
3

1
test/dir/dir/e.txt Normal file
View File

@ -0,0 +1 @@
4

1
test/dir/dir/f.txt Symbolic link
View File

@ -0,0 +1 @@
../f.txt

1
test/dir/e.txt Normal file
View File

@ -0,0 +1 @@
4

1
test/dir/f.txt Normal file
View File

@ -0,0 +1 @@
5

BIN
test/t.zip Normal file

Binary file not shown.

103
unzip.go Normal file
View File

@ -0,0 +1,103 @@
package unzip
import (
"archive/zip"
"io"
"io/ioutil"
"os"
"path/filepath"
)
// Unzip - struct
type Unzip struct {
Src string
Dest string
}
// New - Create a new Unzip.
func New(src string, dest string) Unzip {
return Unzip{src, dest}
}
func writeSymbolicLink(filePath string, targetPath string) error {
err := os.MkdirAll(filepath.Dir(filePath), 0755)
if err != nil {
return err
}
err = os.Symlink(targetPath, filePath)
if err != nil {
return err
}
return nil
}
// Extract - Extract zip file.
func (uz Unzip) Extract() error {
r, err := zip.OpenReader(uz.Src)
if err != nil {
return err
}
defer func() {
if err := r.Close(); err != nil {
panic(err)
}
}()
os.MkdirAll(uz.Dest, 0755)
// Closure to address file descriptors issue with all the deferred .Close() methods
extractAndWriteFile := func(f *zip.File) error {
rc, err := f.Open()
if err != nil {
return err
}
defer func() {
if err := rc.Close(); err != nil {
panic(err)
}
}()
path := filepath.Join(uz.Dest, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(path, f.Mode())
} else {
mode := f.FileHeader.Mode()
if mode&os.ModeType == os.ModeSymlink {
data, err := ioutil.ReadAll(rc)
if err != nil {
return err
}
writeSymbolicLink(path, string(data))
} else {
os.MkdirAll(filepath.Dir(path), f.Mode())
outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer func() {
if err := outFile.Close(); err != nil {
panic(err)
}
}()
_, err = io.Copy(outFile, rc)
if err != nil {
return err
}
}
}
return nil
}
for _, f := range r.File {
err := extractAndWriteFile(f)
if err != nil {
return err
}
}
return nil
}

299
unzip_test.go Normal file
View File

@ -0,0 +1,299 @@
package unzip
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
)
func currentDir() string {
dir, err := os.Getwd()
if err != nil {
return ""
}
return dir
}
func testNullContent(t *testing.T) {
aFile, err := os.Open(path.Join(currentDir(), "./test/a.txt"))
if err != nil {
t.Fatal(err)
}
defer aFile.Close()
aFileContent, err := ioutil.ReadAll(aFile)
if err != nil {
t.Fatal(err)
}
aOutFile, err := os.Open(path.Join(currentDir(), "./test/out/a.txt"))
if err != nil {
t.Fatal(err)
}
defer aOutFile.Close()
aOutFileContent, err := ioutil.ReadAll(aOutFile)
if err != nil {
t.Fatal(err)
}
if string(aFileContent) != string(aOutFileContent) {
t.Fatal("Unzip file content error.")
}
if string(aFileContent) != "" {
t.Fatal("Unzip file content error.")
}
}
func testContentEqual(t *testing.T) {
bFile, err := os.Open(path.Join(currentDir(), "./test/b.txt"))
if err != nil {
t.Fatal(err)
}
defer bFile.Close()
bFileContent, err := ioutil.ReadAll(bFile)
if err != nil {
t.Fatal(err)
}
bOutFile, err := os.Open(path.Join(currentDir(), "./test/out/b.txt"))
if err != nil {
t.Fatal(err)
}
defer bOutFile.Close()
bOutFileContent, err := ioutil.ReadAll(bOutFile)
if err != nil {
t.Fatal(err)
}
if string(bFileContent) != string(bOutFileContent) {
t.Log(string(bFileContent))
t.Log(string(bOutFileContent))
t.Fatal("Unzip file content error.")
}
}
func testLink(t *testing.T) {
cOutFile, err := os.OpenFile(path.Join(currentDir(), "./test/out/c.txt"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
t.Fatal(err)
}
defer cOutFile.Close()
cOutFileContent, err := ioutil.ReadAll(cOutFile)
if err != nil {
t.Fatal(err)
}
cOutLinkFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/c.txt"))
if err != nil {
t.Fatal(err)
}
defer cOutLinkFile.Close()
cOutLinkFileContent, err := ioutil.ReadAll(cOutLinkFile)
if err != nil {
t.Fatal(err)
}
if string(cOutFileContent) != string(cOutLinkFileContent) {
t.Fatal("Unzip file content error or link file content error.")
}
t.Log("Old c.txt content:", string(cOutFileContent))
t.Log("Change c.txt content.")
_, err = cOutFile.WriteString("123")
if err != nil {
t.Fatal(err)
}
cOutFileNew, err := os.Open(path.Join(currentDir(), "./test/out/c.txt"))
if err != nil {
t.Fatal(err)
}
defer cOutFileNew.Close()
cOutFileContentNew, err := ioutil.ReadAll(cOutFileNew)
if err != nil {
t.Fatal(err)
}
t.Log("New c.txt content:", string(cOutFileContentNew))
cOutLinkFileNew, err := os.Open(path.Join(currentDir(), "./test/out/dir/c.txt"))
if err != nil {
t.Fatal(err)
}
defer cOutLinkFileNew.Close()
cOutLinkFileContentNew, err := ioutil.ReadAll(cOutLinkFileNew)
if err != nil {
t.Fatal(err)
}
if string(cOutFileContentNew) != string(cOutLinkFileContentNew) {
t.Fatal("Unzip file link error.")
}
}
func testSymlink(t *testing.T) {
targetURL, err := filepath.EvalSymlinks(path.Join(currentDir(), "./test/out/d.txt"))
if err != nil {
t.Fatal(err)
}
t.Log("targetURL:", targetURL)
if targetURL != path.Join(currentDir(), "./test/out/dir/d.txt") {
t.Fatal("Unzip file Symlink error.")
}
dOutSymlinkFile, err := os.Open(path.Join(currentDir(), "./test/out/d.txt"))
if err != nil {
t.Fatal(err)
}
defer dOutSymlinkFile.Close()
dOutSymlinkFileContent, err := ioutil.ReadAll(dOutSymlinkFile)
if err != nil {
t.Fatal(err)
}
dOutFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/d.txt"))
if err != nil {
t.Fatal(err)
}
defer dOutFile.Close()
dOutFileContent, err := ioutil.ReadAll(dOutFile)
if err != nil {
t.Fatal(err)
}
if string(dOutSymlinkFileContent) != string(dOutFileContent) {
t.Fatal("The content of symlink file is not equal to that of target file")
}
}
func testLintInDir(t *testing.T) {
eOutFile, err := os.OpenFile(path.Join(currentDir(), "./test/out/dir/dir/e.txt"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
t.Fatal(err)
}
defer eOutFile.Close()
eOutFileContent, err := ioutil.ReadAll(eOutFile)
if err != nil {
t.Fatal(err)
}
eOutLinkFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/e.txt"))
if err != nil {
t.Fatal(err)
}
defer eOutLinkFile.Close()
eOutLinkFileContent, err := ioutil.ReadAll(eOutLinkFile)
if err != nil {
t.Fatal(err)
}
if string(eOutFileContent) != string(eOutLinkFileContent) {
t.Fatal("Unzip file content error or link file content error.")
}
t.Log("Old dir/dir/e.txt content:", string(eOutFileContent))
t.Log("Change dir/dir/e.txt content.")
_, err = eOutFile.WriteString("abcde")
if err != nil {
t.Fatal(err)
}
eOutFileNew, err := os.Open(path.Join(currentDir(), "./test/out/dir/dir/e.txt"))
if err != nil {
t.Fatal(err)
}
defer eOutFileNew.Close()
eOutFileContentNew, err := ioutil.ReadAll(eOutFileNew)
if err != nil {
t.Fatal(err)
}
t.Log("New dir/dir/e.txt content:", string(eOutFileContentNew))
eOutLinkFileNew, err := os.Open(path.Join(currentDir(), "./test/out/dir/e.txt"))
if err != nil {
t.Fatal(err)
}
defer eOutLinkFileNew.Close()
eOutLinkFileContentNew, err := ioutil.ReadAll(eOutLinkFileNew)
if err != nil {
t.Fatal(err)
}
if string(eOutFileContentNew) != string(eOutLinkFileContentNew) {
t.Fatal("Unzip file link error.")
}
}
func testSymlinkInDir(t *testing.T) {
inDirTargetURL, err := filepath.EvalSymlinks(path.Join(currentDir(), "./test/out/dir/dir/f.txt"))
if err != nil {
t.Fatal(err)
}
t.Log("F outSymlinkFile link:", inDirTargetURL)
if inDirTargetURL != path.Join(currentDir(), "./test/out/dir/f.txt") {
t.Fatal("Unzip file Symlink error.")
}
fOutSymlinkFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/dir/f.txt"))
if err != nil {
t.Fatal(err)
}
defer fOutSymlinkFile.Close()
fOutSymlinkFileContent, err := ioutil.ReadAll(fOutSymlinkFile)
if err != nil {
t.Fatal(err)
}
fOutFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/f.txt"))
if err != nil {
t.Fatal(err)
}
defer fOutFile.Close()
fOutFileContent, err := ioutil.ReadAll(fOutFile)
if err != nil {
t.Fatal(err)
}
if string(fOutSymlinkFileContent) != string(fOutFileContent) {
t.Fatal("The content of symlink file is not equal to that of target file")
}
}
func TestUnzip(t *testing.T) {
filePath := filepath.FromSlash(path.Join(currentDir(), "./test/t.zip"))
outDir := filepath.FromSlash(path.Join(currentDir(), "./test/out") + "/")
os.RemoveAll(outDir)
unzip := New(filePath, outDir)
err := unzip.Extract()
if err != nil {
t.Fatal(err)
}
t.Run("Content is null", testNullContent)
t.Run("Content is equal", testContentEqual)
// t.Run("Link", testLink)
t.Run("Symlink", testSymlink)
// t.Run("Link in dir", testLintInDir)
t.Run("Symlink in dir", testSymlinkInDir)
}