From 1521c17fb2de89e6c654fab2842754cceef9ab61 Mon Sep 17 00:00:00 2001 From: lan-yonghui Date: Tue, 9 Sep 2025 19:08:06 +0800 Subject: [PATCH] fix: Fix file decompression issue --- agent/utils/files/file_op.go | 47 ++++++++++++++++++- agent/utils/files/fileinfo.go | 2 + agent/utils/files/tar_gz.go | 4 ++ frontend/src/enums/files.ts | 8 +++- frontend/src/global/mimetype.ts | 1 + .../host/file-management/decompress/index.vue | 13 +---- .../src/views/host/file-management/index.vue | 14 ++++-- 7 files changed, 70 insertions(+), 19 deletions(-) diff --git a/agent/utils/files/file_op.go b/agent/utils/files/file_op.go index 7e6c7040abe9..ffa25e97b412 100644 --- a/agent/utils/files/file_op.go +++ b/agent/utils/files/file_op.go @@ -3,6 +3,7 @@ package files import ( "archive/zip" "bufio" + "compress/gzip" "context" "crypto/tls" "encoding/json" @@ -597,10 +598,10 @@ func getFormat(cType CompressType) archiver.CompressedArchive { format.Archival = archiver.Zip{ Compression: zip.Deflate, } - case Bz2: + case Bz2, TarBz2: format.Compression = archiver.Bz2{} format.Archival = archiver.Tar{} - case Xz: + case Xz, TarXz: format.Compression = archiver.Xz{} format.Archival = archiver.Tar{} } @@ -680,6 +681,13 @@ func decodeGBK(input string) (string, error) { func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType) error { format := getFormat(cType) + if cType == Gz { + err := f.DecompressGzFile(srcFile, dst) + if err != nil { + return err + } + } + handler := func(ctx context.Context, archFile archiver.File) error { info := archFile.FileInfo if isIgnoreFile(archFile.Name()) { @@ -794,6 +802,41 @@ func ZipFile(files []archiver.File, dst afero.File) error { return nil } +func (f FileOp) DecompressGzFile(srcFile, dst string) error { + in, err := f.Fs.Open(srcFile) + if err != nil { + return fmt.Errorf("open source file failed: %w", err) + } + defer in.Close() + + gr, err := gzip.NewReader(in) + if err != nil { + return fmt.Errorf("gzip reader creation failed: %w", err) + } + defer gr.Close() + + outName := strings.TrimSuffix(filepath.Base(srcFile), ".gz") + outPath := filepath.Join(dst, outName) + parentDir := filepath.Dir(outPath) + if !f.Stat(parentDir) { + if err := f.Fs.MkdirAll(parentDir, 0755); err != nil { + return err + } + } + + fw, err := f.Fs.OpenFile(outPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) + if err != nil { + return fmt.Errorf("create output file failed: %w", err) + } + defer fw.Close() + + if _, err := io.Copy(fw, gr); err != nil { + return fmt.Errorf("copy content failed: %w", err) + } + + return nil +} + func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules string) error { if !f.Stat(path.Dir(dst)) { if err := f.Fs.MkdirAll(path.Dir(dst), constant.FilePerm); err != nil { diff --git a/agent/utils/files/fileinfo.go b/agent/utils/files/fileinfo.go index a234656b8cb0..7022544ad885 100644 --- a/agent/utils/files/fileinfo.go +++ b/agent/utils/files/fileinfo.go @@ -460,9 +460,11 @@ const ( Zip CompressType = "zip" Gz CompressType = "gz" Bz2 CompressType = "bz2" + TarBz2 CompressType = "tar.bz2" Tar CompressType = "tar" TarGz CompressType = "tar.gz" Xz CompressType = "xz" + TarXz CompressType = "tar.xz" SdkZip CompressType = "sdkZip" SdkTarGz CompressType = "sdkTarGz" Rar CompressType = "rar" diff --git a/agent/utils/files/tar_gz.go b/agent/utils/files/tar_gz.go index 83d36bd397d7..0122712d2b77 100644 --- a/agent/utils/files/tar_gz.go +++ b/agent/utils/files/tar_gz.go @@ -2,6 +2,7 @@ package files import ( "fmt" + "os" "path/filepath" "strings" @@ -17,6 +18,9 @@ func NewTarGzArchiver() ShellArchiver { } func (t TarGzArchiver) Extract(filePath, dstDir string, secret string) error { + if err := os.MkdirAll(dstDir, 0755); err != nil { + return fmt.Errorf("failed to create destination dir: %w", err) + } var err error commands := "" if len(secret) != 0 { diff --git a/frontend/src/enums/files.ts b/frontend/src/enums/files.ts index 38598cf148de..2f87dccd9b7c 100644 --- a/frontend/src/enums/files.ts +++ b/frontend/src/enums/files.ts @@ -2,10 +2,12 @@ export enum CompressType { Zip = 'zip', Gz = 'gz', Bz2 = 'bz2', + TarBz2 = 'tar.bz2', Tar = 'tar', TGz = 'tgz', TarGz = 'tar.gz', Xz = 'xz', + TarXz = 'tar.xz', Rar = 'rar', '7z' = '7z', } @@ -13,11 +15,13 @@ export enum CompressType { export enum CompressExtension { zip = '.zip', gz = '.gz', - bz2 = '.tar.bz2', + 'tar.bz2' = '.tar.bz2', + bz2 = '.bz2', tar = '.tar', tgz = '.tgz', 'tar.gz' = '.tar.gz', - xz = '.tar.xz', + 'tar.xz' = '.tar.xz', + xz = '.xz', rar = '.rar', '7z' = '.7z', } diff --git a/frontend/src/global/mimetype.ts b/frontend/src/global/mimetype.ts index 5abedd220929..f838f6d5fcc0 100644 --- a/frontend/src/global/mimetype.ts +++ b/frontend/src/global/mimetype.ts @@ -18,6 +18,7 @@ export const Mimetypes = new Map([ ['application/octet-stream', CompressType.Tar], ['application/x-rar-compressed', CompressType.Rar], ['application/vnd.rar', CompressType.Rar], + ['application/rar', CompressType.Rar], ['application/x-7z-compressed', CompressType['7z']], ]); diff --git a/frontend/src/views/host/file-management/decompress/index.vue b/frontend/src/views/host/file-management/decompress/index.vue index bf9b92d6301f..032bc34e6d0d 100644 --- a/frontend/src/views/host/file-management/decompress/index.vue +++ b/frontend/src/views/host/file-management/decompress/index.vue @@ -39,7 +39,6 @@ import { File } from '@/api/interface/file'; import { FormInstance, FormRules } from 'element-plus'; import { Rules } from '@/global/form-rules'; import { deCompressFile } from '@/api/modules/files'; -import { Mimetypes } from '@/global/mimetype'; import FileList from '@/components/file-list/index.vue'; import { MsgSuccess } from '@/utils/message'; @@ -48,7 +47,7 @@ interface CompressProps { dst: string; name: string; path: string; - mimeType: string; + type: string; } const rules = reactive({ @@ -72,14 +71,6 @@ const handleClose = () => { em('close', open); }; -const getFileType = (mime: string): string => { - if (Mimetypes.get(mime) != undefined) { - return String(Mimetypes.get(mime)); - } else { - return ''; - } -}; - const getLinkPath = (path: string) => { form.value.dst = path; }; @@ -103,7 +94,7 @@ const submit = async (formEl: FormInstance | undefined) => { }; const acceptParams = (props: CompressProps) => { - form.value.type = getFileType(props.mimeType); + form.value.type = props.type; form.value.dst = props.dst; form.value.path = props.path; name.value = props.name; diff --git a/frontend/src/views/host/file-management/index.vue b/frontend/src/views/host/file-management/index.vue index 98004e75524c..f07215113036 100644 --- a/frontend/src/views/host/file-management/index.vue +++ b/frontend/src/views/host/file-management/index.vue @@ -688,7 +688,7 @@ import VscodeOpenDialog from '@/components/vscode-open/index.vue'; import { debounce } from 'lodash-es'; import TerminalDialog from './terminal/index.vue'; import { Dashboard } from '@/api/interface/dashboard'; -import { CompressExtension, MimetypeByExtensionObject } from '@/enums/files'; +import { CompressExtension } from '@/enums/files'; import type { TabPaneName } from 'element-plus'; const globalStore = GlobalStore(); @@ -733,7 +733,7 @@ let pointer = -1; const fileCreate = reactive({ path: '/', isDir: false, mode: 0o755 }); const fileCompress = reactive({ files: [''], name: '', dst: '', operate: 'compress' }); -const fileDeCompress = reactive({ path: '', name: '', dst: '', mimeType: '' }); +const fileDeCompress = reactive({ path: '', name: '', dst: '', type: '' }); const fileEdit = reactive({ content: '', path: '', name: '', language: 'plaintext', extension: '' }); const filePreview = reactive({ path: '', name: '', extension: '', fileType: '', imageFiles: [], currentNode: '' }); const codeReq = reactive({ path: '', expand: false, page: 1, pageSize: 100, isDetail: false }); @@ -1186,9 +1186,9 @@ const openDeCompress = (item: File.File) => { MsgWarning(i18n.global.t('file.canNotDeCompress')); return; } - fileDeCompress.mimeType = item.mimeType; + fileDeCompress.type = Mimetypes.get(item.mimeType); if (CompressExtension[Mimetypes.get(item.mimeType)] != item.extension) { - fileDeCompress.mimeType = MimetypeByExtensionObject[item.extension]; + fileDeCompress.type = getEnumKeyByValue(item.extension); } fileDeCompress.name = item.name; @@ -1198,6 +1198,12 @@ const openDeCompress = (item: File.File) => { deCompressRef.value.acceptParams(fileDeCompress); }; +function getEnumKeyByValue(value: string): keyof typeof CompressExtension | undefined { + return (Object.keys(CompressExtension) as Array).find( + (k) => CompressExtension[k] === value, + ); +} + const openView = (item: File.File) => { const fileType = getFileType(item.extension); if (fileType === 'image') {