mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-29 02:10:58 +08:00
fix(file): 补全单文件格式压缩入口并适配压缩测试
.bz2/.xz/.zst 后缀已被识别为单文件压缩格式,但 Compress 未提供 对应入口导致报 unsupported format,与 .gz 一并收敛为统一分支, 解压侧同步收敛,测试改为表驱动覆盖归档与单文件两类格式 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+27
-25
@@ -47,12 +47,12 @@ func Compress(dir string, src []string, dst string) error {
|
||||
switch format {
|
||||
case Zip:
|
||||
_, err = shell.ExecfWithDir(dir, "zip -qr -o %s %s", dst, strings.Join(src, " "))
|
||||
case Gz:
|
||||
// gzip 仅支持压缩单个文件
|
||||
case Gz, Bz2, Xz, Zst:
|
||||
// 单文件压缩格式仅支持压缩单个文件
|
||||
if len(src) != 1 {
|
||||
return errors.New("gz format only supports compressing a single file")
|
||||
return fmt.Errorf("%s format only supports compressing a single file", format)
|
||||
}
|
||||
_, err = shell.ExecfWithDir(dir, "gzip -c %s > %s", src[0], dst)
|
||||
_, err = shell.ExecfWithDir(dir, "%s -c %s > %s", compressorByFormat(format), src[0], dst)
|
||||
case TGz:
|
||||
_, err = shell.ExecfWithDir(dir, "tar -czf %s %s", dst, strings.Join(src, " "))
|
||||
case TBz2:
|
||||
@@ -90,11 +90,10 @@ func UnCompress(src string, dst string) error {
|
||||
case Zip:
|
||||
// 用 7z 解压 zip,自动检测文件名编码,避免中文文件名变成 #Uxxxx
|
||||
_, err = shell.Execf("7z x -y '%s' -o'%s'", src, dst)
|
||||
case Gz:
|
||||
// 单独的 gzip 文件(如 .sql.gz),解压到目标目录
|
||||
// gunzip -k 保留原文件,-c 输出到标准输出,重定向到目标文件
|
||||
baseName := strings.TrimSuffix(filepath.Base(src), ".gz")
|
||||
_, err = shell.Execf("gunzip -c '%s' > '%s'", src, filepath.Join(dst, baseName))
|
||||
case Gz, Bz2, Xz, Zst:
|
||||
// 单独压缩的文件(如 .sql.gz),解压到目标目录
|
||||
baseName := strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
|
||||
_, err = shell.Execf("%s -dc '%s' > '%s'", compressorByFormat(format), src, filepath.Join(dst, baseName))
|
||||
case TGz:
|
||||
_, err = shell.Execf("tar -xzf '%s' -C '%s'", src, dst)
|
||||
case TBz2:
|
||||
@@ -103,22 +102,10 @@ func UnCompress(src string, dst string) error {
|
||||
_, err = shell.Execf("tar -xf '%s' -C '%s'", src, dst)
|
||||
case TXz:
|
||||
_, err = shell.Execf("tar -xJf '%s' -C '%s'", src, dst)
|
||||
case Xz:
|
||||
// 单独的 xz 文件(如 .sql.xz),解压到目标目录
|
||||
baseName := strings.TrimSuffix(filepath.Base(src), ".xz")
|
||||
_, err = shell.Execf("xz -dc '%s' > '%s'", src, filepath.Join(dst, baseName))
|
||||
case Bz2:
|
||||
// 单独的 bzip2 文件(如 .sql.bz2),解压到目标目录
|
||||
baseName := strings.TrimSuffix(filepath.Base(src), ".bz2")
|
||||
_, err = shell.Execf("bzip2 -dc '%s' > '%s'", src, filepath.Join(dst, baseName))
|
||||
case SevenZip:
|
||||
_, err = shell.Execf("7z x -y '%s' -o'%s'", src, dst)
|
||||
case TZst:
|
||||
_, err = shell.Execf("tar --zstd -xf '%s' -C '%s'", src, dst)
|
||||
case Zst:
|
||||
// 单独的 zstd 文件(如 .sql.zst),解压到目标目录
|
||||
baseName := strings.TrimSuffix(filepath.Base(src), ".zst")
|
||||
_, err = shell.Execf("zstd -dc '%s' > '%s'", src, filepath.Join(dst, baseName))
|
||||
default:
|
||||
return errors.New("unsupported format")
|
||||
}
|
||||
@@ -145,12 +132,12 @@ func CompressShell(dir string, src []string, dst string) (string, error) {
|
||||
switch format {
|
||||
case Zip:
|
||||
cmd = fmt.Sprintf("zip -qr -o '%s' %s", dst, sources)
|
||||
case Gz:
|
||||
// gzip 仅支持压缩单个文件
|
||||
case Gz, Bz2, Xz, Zst:
|
||||
// 单文件压缩格式仅支持压缩单个文件
|
||||
if len(src) != 1 {
|
||||
return "", errors.New("gz format only supports compressing a single file")
|
||||
return "", fmt.Errorf("%s format only supports compressing a single file", format)
|
||||
}
|
||||
cmd = fmt.Sprintf("gzip -c %s > '%s'", sources, dst)
|
||||
cmd = fmt.Sprintf("%s -c %s > '%s'", compressorByFormat(format), sources, dst)
|
||||
case TGz:
|
||||
cmd = fmt.Sprintf("tar -czf '%s' %s", dst, sources)
|
||||
case TBz2:
|
||||
@@ -245,6 +232,21 @@ func ListCompress(src string) ([]string, error) {
|
||||
return strings.Split(out, "\n"), nil
|
||||
}
|
||||
|
||||
// compressorByFormat 单文件压缩格式对应的压缩工具,压缩用 -c,解压用 -dc
|
||||
func compressorByFormat(format FormatArchive) string {
|
||||
switch format {
|
||||
case Gz:
|
||||
return "gzip"
|
||||
case Bz2:
|
||||
return "bzip2"
|
||||
case Xz:
|
||||
return "xz"
|
||||
case Zst:
|
||||
return "zstd"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// formatArchiveByPath 根据文件后缀获取压缩格式
|
||||
func formatArchiveByPath(path string) (FormatArchive, error) {
|
||||
switch filepath.Ext(path) {
|
||||
|
||||
+53
-139
@@ -3,6 +3,7 @@ package io
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/libtnb/utils/env"
|
||||
@@ -52,164 +53,77 @@ func (s *IOTestSuite) TestWriteAppendAppendsToFile() {
|
||||
s.Equal("Hello, World!", content)
|
||||
}
|
||||
|
||||
// archiveExts 归档格式,支持多文件压缩
|
||||
var archiveExts = []string{".zip", ".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tar.xz", ".tar.zst", ".7z"}
|
||||
|
||||
// singleExts 单文件压缩格式,仅支持压缩单个文件
|
||||
var singleExts = []string{".gz", ".bz2", ".xz", ".zst"}
|
||||
|
||||
func (s *IOTestSuite) TestCompress() {
|
||||
abs, err := filepath.Abs("testdata")
|
||||
s.NoError(err)
|
||||
src := []string{"compress_test1.txt", "compress_test2.txt"}
|
||||
err = Write(filepath.Join(abs, src[0]), "File 1", 0644)
|
||||
s.NoError(err)
|
||||
err = Write(filepath.Join(abs, src[1]), "File 2", 0644)
|
||||
s.NoError(err)
|
||||
s.NoError(Write(filepath.Join(abs, src[0]), "File 1", 0644))
|
||||
s.NoError(Write(filepath.Join(abs, src[1]), "File 2", 0644))
|
||||
|
||||
err = Compress(abs, src, filepath.Join(abs, "compress_test.zip"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "compress_test.bz2"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "compress_test.tar"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "compress_test.tar.gz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "compress_test.tgz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "compress_test.xz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "compress_test.7z"))
|
||||
s.NoError(err)
|
||||
|
||||
s.NoError(Remove("testdata"))
|
||||
for _, ext := range archiveExts {
|
||||
s.NoError(Compress(abs, src, filepath.Join(abs, "compress_test"+ext)), ext)
|
||||
}
|
||||
for _, ext := range singleExts {
|
||||
s.NoError(Compress(abs, src[:1], filepath.Join(abs, "compress_single"+ext)), ext)
|
||||
s.Error(Compress(abs, src, filepath.Join(abs, "compress_multi"+ext)), ext)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IOTestSuite) TestUnCompress() {
|
||||
abs, err := filepath.Abs("testdata")
|
||||
s.NoError(err)
|
||||
src := []string{"uncompress_test1.txt", "uncompress_test2.txt"}
|
||||
err = Write(filepath.Join(abs, src[0]), "File 1", 0644)
|
||||
s.NoError(err)
|
||||
err = Write(filepath.Join(abs, src[1]), "File 2", 0644)
|
||||
s.NoError(err)
|
||||
s.NoError(Write(filepath.Join(abs, src[0]), "File 1", 0644))
|
||||
s.NoError(Write(filepath.Join(abs, src[1]), "File 2", 0644))
|
||||
|
||||
err = Compress(abs, src, filepath.Join(abs, "uncompress_test.zip"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "uncompress_test.bz2"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "uncompress_test.tar"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "uncompress_test.tar.gz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "uncompress_test.tgz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "uncompress_test.xz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "uncompress_test.7z"))
|
||||
s.NoError(err)
|
||||
|
||||
err = UnCompress(filepath.Join(abs, "uncompress_test.zip"), filepath.Join(abs, "uncompressed_zip"))
|
||||
s.NoError(err)
|
||||
data, err := Read("testdata/uncompressed_zip/uncompress_test1.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 1", data)
|
||||
data, err = Read("testdata/uncompressed_zip/uncompress_test2.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 2", data)
|
||||
err = UnCompress(filepath.Join(abs, "uncompress_test.bz2"), filepath.Join(abs, "uncompressed_bz2"))
|
||||
s.NoError(err)
|
||||
data, err = Read("testdata/uncompressed_bz2/uncompress_test1.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 1", data)
|
||||
data, err = Read("testdata/uncompressed_bz2/uncompress_test2.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 2", data)
|
||||
err = UnCompress(filepath.Join(abs, "uncompress_test.tar"), filepath.Join(abs, "uncompressed_tar"))
|
||||
s.NoError(err)
|
||||
data, err = Read("testdata/uncompressed_tar/uncompress_test1.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 1", data)
|
||||
data, err = Read("testdata/uncompressed_tar/uncompress_test2.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 2", data)
|
||||
err = UnCompress(filepath.Join(abs, "uncompress_test.tar.gz"), filepath.Join(abs, "uncompressed_tar_gz"))
|
||||
s.NoError(err)
|
||||
data, err = Read("testdata/uncompressed_tar_gz/uncompress_test1.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 1", data)
|
||||
data, err = Read("testdata/uncompressed_tar_gz/uncompress_test2.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 2", data)
|
||||
err = UnCompress(filepath.Join(abs, "uncompress_test.tgz"), filepath.Join(abs, "uncompressed_tgz"))
|
||||
s.NoError(err)
|
||||
data, err = Read("testdata/uncompressed_tgz/uncompress_test1.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 1", data)
|
||||
data, err = Read("testdata/uncompressed_tgz/uncompress_test2.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 2", data)
|
||||
err = UnCompress(filepath.Join(abs, "uncompress_test.xz"), filepath.Join(abs, "uncompressed_xz"))
|
||||
s.NoError(err)
|
||||
data, err = Read("testdata/uncompressed_xz/uncompress_test1.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 1", data)
|
||||
data, err = Read("testdata/uncompressed_xz/uncompress_test2.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 2", data)
|
||||
err = UnCompress(filepath.Join(abs, "uncompress_test.7z"), filepath.Join(abs, "uncompressed_7z"))
|
||||
s.NoError(err)
|
||||
data, err = Read("testdata/uncompressed_7z/uncompress_test1.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 1", data)
|
||||
data, err = Read("testdata/uncompressed_7z/uncompress_test2.txt")
|
||||
s.NoError(err)
|
||||
s.Equal("File 2", data)
|
||||
|
||||
s.NoError(Remove("testdata"))
|
||||
for _, ext := range archiveExts {
|
||||
dst := filepath.Join(abs, "uncompressed"+strings.ReplaceAll(ext, ".", "_"))
|
||||
s.NoError(Compress(abs, src, filepath.Join(abs, "uncompress_test"+ext)), ext)
|
||||
s.NoError(UnCompress(filepath.Join(abs, "uncompress_test"+ext), dst), ext)
|
||||
data, err := Read(filepath.Join(dst, src[0]))
|
||||
s.NoError(err, ext)
|
||||
s.Equal("File 1", data, ext)
|
||||
data, err = Read(filepath.Join(dst, src[1]))
|
||||
s.NoError(err, ext)
|
||||
s.Equal("File 2", data, ext)
|
||||
}
|
||||
// 单文件压缩格式解压后去掉压缩后缀恢复原文件名
|
||||
for _, ext := range singleExts {
|
||||
dst := filepath.Join(abs, "uncompressed_single"+strings.ReplaceAll(ext, ".", "_"))
|
||||
s.NoError(Compress(abs, src[:1], filepath.Join(abs, src[0]+ext)), ext)
|
||||
s.NoError(UnCompress(filepath.Join(abs, src[0]+ext), dst), ext)
|
||||
data, err := Read(filepath.Join(dst, src[0]))
|
||||
s.NoError(err, ext)
|
||||
s.Equal("File 1", data, ext)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IOTestSuite) TestListCompress() {
|
||||
abs, err := filepath.Abs("testdata")
|
||||
s.NoError(err)
|
||||
src := []string{"list_archive_test1.txt", "list_archive_test2.txt"}
|
||||
err = Write(filepath.Join(abs, src[0]), "File 1", 0644)
|
||||
s.NoError(err)
|
||||
err = Write(filepath.Join(abs, src[1]), "File 2", 0644)
|
||||
s.NoError(err)
|
||||
s.NoError(Write(filepath.Join(abs, src[0]), "File 1", 0644))
|
||||
s.NoError(Write(filepath.Join(abs, src[1]), "File 2", 0644))
|
||||
|
||||
err = Compress(abs, src, filepath.Join(abs, "list_archive_test.zip"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "list_archive_test.bz2"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "list_archive_test.tar"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "list_archive_test.tar.gz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "list_archive_test.tgz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "list_archive_test.xz"))
|
||||
s.NoError(err)
|
||||
err = Compress(abs, src, filepath.Join(abs, "list_archive_test.7z"))
|
||||
s.NoError(err)
|
||||
|
||||
list, err := ListCompress(filepath.Join(abs, "list_archive_test.zip"))
|
||||
s.NoError(err)
|
||||
s.Len(list, 2)
|
||||
list, err = ListCompress(filepath.Join(abs, "list_archive_test.bz2"))
|
||||
s.NoError(err)
|
||||
s.Len(list, 2)
|
||||
list, err = ListCompress(filepath.Join(abs, "list_archive_test.tar"))
|
||||
s.NoError(err)
|
||||
s.Len(list, 2)
|
||||
list, err = ListCompress(filepath.Join(abs, "list_archive_test.tar.gz"))
|
||||
s.NoError(err)
|
||||
s.Len(list, 2)
|
||||
list, err = ListCompress(filepath.Join(abs, "list_archive_test.tgz"))
|
||||
s.NoError(err)
|
||||
s.Len(list, 2)
|
||||
list, err = ListCompress(filepath.Join(abs, "list_archive_test.xz"))
|
||||
s.NoError(err)
|
||||
s.Len(list, 2)
|
||||
list, err = ListCompress(filepath.Join(abs, "list_archive_test.7z"))
|
||||
s.NoError(err)
|
||||
s.Len(list, 2)
|
||||
|
||||
s.NoError(Remove("testdata"))
|
||||
for _, ext := range archiveExts {
|
||||
s.NoError(Compress(abs, src, filepath.Join(abs, "list_archive_test"+ext)), ext)
|
||||
list, err := ListCompress(filepath.Join(abs, "list_archive_test"+ext))
|
||||
s.NoError(err, ext)
|
||||
s.Len(list, 2, ext)
|
||||
}
|
||||
// 单文件压缩格式返回去掉压缩后缀的文件名
|
||||
for _, ext := range singleExts {
|
||||
s.NoError(Compress(abs, src[:1], filepath.Join(abs, src[0]+ext)), ext)
|
||||
list, err := ListCompress(filepath.Join(abs, src[0]+ext))
|
||||
s.NoError(err, ext)
|
||||
s.Equal([]string{src[0]}, list, ext)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IOTestSuite) TestRemoveDeletesFileOrDirectory() {
|
||||
|
||||
Reference in New Issue
Block a user