mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-17 03:04:46 +08:00
feat: 增加文件下载功能
This commit is contained in:
parent
04db6a8cf3
commit
16b9adfefe
@ -167,3 +167,16 @@ func (b *BaseApi) ChangeName(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) Download(c *gin.Context) {
|
||||||
|
var req dto.FileDownload
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := fileService.Download(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
@ -57,3 +57,9 @@ type FileRename struct {
|
|||||||
OldName string
|
OldName string
|
||||||
NewName string
|
NewName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileDownload struct {
|
||||||
|
Url string `json:"url" validate:"required"`
|
||||||
|
Path string `json:"path" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
}
|
||||||
|
@ -120,6 +120,11 @@ func (f FileService) ChangeName(c dto.FileRename) error {
|
|||||||
return fo.Rename(c.OldName, c.NewName)
|
return fo.Rename(c.OldName, c.NewName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f FileService) Download(c dto.FileDownload) error {
|
||||||
|
fo := files.NewFileOp()
|
||||||
|
return fo.DownloadFile(c.Url, filepath.Join(c.Path, c.Name))
|
||||||
|
}
|
||||||
|
|
||||||
func getUuid() string {
|
func getUuid() string {
|
||||||
b := make([]byte, 16)
|
b := make([]byte, 16)
|
||||||
io.ReadFull(rand.Reader, b)
|
io.ReadFull(rand.Reader, b)
|
||||||
|
@ -26,6 +26,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
|
|||||||
fileRouter.POST("/save", baseApi.SaveContent)
|
fileRouter.POST("/save", baseApi.SaveContent)
|
||||||
fileRouter.POST("/upload", baseApi.UploadFiles)
|
fileRouter.POST("/upload", baseApi.UploadFiles)
|
||||||
fileRouter.POST("/rename", baseApi.ChangeName)
|
fileRouter.POST("/rename", baseApi.ChangeName)
|
||||||
|
fileRouter.POST("/download", baseApi.Download)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@ package files
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/1Panel-dev/1Panel/global"
|
||||||
"github.com/mholt/archiver/v4"
|
"github.com/mholt/archiver/v4"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@ -81,6 +83,28 @@ func (f FileOp) Rename(oldName string, newName string) error {
|
|||||||
return f.Fs.Rename(oldName, newName)
|
return f.Fs.Rename(oldName, newName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f FileOp) DownloadFile(url, dst string) error {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if _, err = io.Copy(out, resp.Body); err != nil {
|
||||||
|
global.LOG.Errorf("save download file [%s] error, err %s", dst, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type CompressType string
|
type CompressType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -63,6 +63,9 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
|
|||||||
}
|
}
|
||||||
if file.IsSymlink {
|
if file.IsSymlink {
|
||||||
file.LinkPath = GetSymlink(op.Path)
|
file.LinkPath = GetSymlink(op.Path)
|
||||||
|
}
|
||||||
|
if op.Search != "" {
|
||||||
|
|
||||||
}
|
}
|
||||||
if op.Expand {
|
if op.Expand {
|
||||||
if file.IsDir {
|
if file.IsDir {
|
||||||
|
@ -69,4 +69,10 @@ export namespace File {
|
|||||||
oldName: string;
|
oldName: string;
|
||||||
newName: string;
|
newName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FileDownload {
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,3 +44,7 @@ export const UploadFileData = (params: FormData) => {
|
|||||||
export const RenameRile = (params: File.FileRename) => {
|
export const RenameRile = (params: File.FileRename) => {
|
||||||
return http.post<File.File>('files/rename', params);
|
return http.post<File.File>('files/rename', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DownloadFile = (params: File.FileDownload) => {
|
||||||
|
return http.post<File.File>('files/download', params);
|
||||||
|
};
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
right: Boolean,
|
right: Boolean,
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@ export enum ResultEnum {
|
|||||||
ERROR = 500,
|
ERROR = 500,
|
||||||
OVERDUE = 401,
|
OVERDUE = 401,
|
||||||
FORBIDDEN = 403,
|
FORBIDDEN = 403,
|
||||||
TIMEOUT = 10000,
|
TIMEOUT = 100000,
|
||||||
TYPE = 'success',
|
TYPE = 'success',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,5 +184,21 @@ export default {
|
|||||||
wRole: 'Write',
|
wRole: 'Write',
|
||||||
xRole: 'executable',
|
xRole: 'executable',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
compress: 'compress',
|
||||||
|
deCompress: 'Decompress',
|
||||||
|
compressType: 'compression format',
|
||||||
|
compressDst: 'compression path',
|
||||||
|
replace: 'Overwrite existing file',
|
||||||
|
compressSuccess: 'Compression successful',
|
||||||
|
deCompressSuccess: 'Decompression succeeded',
|
||||||
|
deCompressDst: 'Decompression path',
|
||||||
|
linkType: 'Link type',
|
||||||
|
softLink: 'soft link',
|
||||||
|
hardLink: 'hard link',
|
||||||
|
linkPath: 'Link path',
|
||||||
|
selectFile: 'Select file',
|
||||||
|
downloadSuccess: 'Download successful',
|
||||||
|
downloadUrl: 'download URL',
|
||||||
|
downloadStart: 'Download started!',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -197,5 +197,8 @@ export default {
|
|||||||
hardLink: '硬链接',
|
hardLink: '硬链接',
|
||||||
linkPath: '链接路径',
|
linkPath: '链接路径',
|
||||||
selectFile: '选择文件',
|
selectFile: '选择文件',
|
||||||
|
downloadSuccess: '下载成功',
|
||||||
|
downloadUrl: '下载地址',
|
||||||
|
downloadStart: '下载开始!',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
98
frontend/src/views/file-management/file-down/index.vue
Normal file
98
frontend/src/views/file-management/file-down/index.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="open" :before-close="handleClose" :title="$t('file.download')" width="30%" @open="onOpen">
|
||||||
|
<el-form
|
||||||
|
ref="fileForm"
|
||||||
|
label-position="left"
|
||||||
|
:model="addForm"
|
||||||
|
label-width="100px"
|
||||||
|
:rules="rules"
|
||||||
|
v-loading="loading"
|
||||||
|
>
|
||||||
|
<el-form-item :label="$t('file.downloadUrl')" prop="url">
|
||||||
|
<el-input v-model="addForm.url" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('file.path')" prop="path">
|
||||||
|
<el-input v-model="addForm.path">
|
||||||
|
<template #append> <FileList :path="path" @choose="getPath"></FileList> </template
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('file.name')" prop="name">
|
||||||
|
<el-input v-model="addForm.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submit(fileForm)" :disabled="loading">{{
|
||||||
|
$t('commons.button.confirm')
|
||||||
|
}}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { DownloadFile } from '@/api/modules/files';
|
||||||
|
import { Rules } from '@/global/form-rues';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElMessage, FormInstance, FormRules } from 'element-plus';
|
||||||
|
import { reactive, ref, toRefs } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { open } = toRefs(props);
|
||||||
|
const fileForm = ref<FormInstance>();
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const rules = reactive<FormRules>({
|
||||||
|
name: [Rules.requiredInput],
|
||||||
|
path: [Rules.requiredInput],
|
||||||
|
url: [Rules.requiredInput],
|
||||||
|
});
|
||||||
|
|
||||||
|
const addForm = reactive({
|
||||||
|
url: '',
|
||||||
|
path: '',
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
em('close', open);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPath = (path: string) => {
|
||||||
|
addForm.path = path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOpen = () => {
|
||||||
|
addForm.path = props.path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
await formEl.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loading.value = true;
|
||||||
|
DownloadFile(addForm)
|
||||||
|
.then(() => {
|
||||||
|
ElMessage.success(i18n.global.t('file.downloadStart'));
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
@ -58,8 +58,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<el-button type="primary" plain @click="openUpload"> {{ $t('file.upload') }}</el-button>
|
<el-button type="primary" plain @click="openUpload"> {{ $t('file.upload') }}</el-button>
|
||||||
<el-button type="primary" plain> {{ $t('file.search') }}</el-button>
|
<!-- <el-button type="primary" plain> {{ $t('file.search') }}</el-button> -->
|
||||||
<el-button type="primary" plain> {{ $t('file.remoteFile') }}</el-button>
|
<el-button type="primary" plain @click="openDownload"> {{ $t('file.remoteFile') }}</el-button>
|
||||||
<!-- <el-button type="primary" plain> {{ $t('file.sync') }}</el-button>
|
<!-- <el-button type="primary" plain> {{ $t('file.sync') }}</el-button>
|
||||||
<el-button type="primary" plain> {{ $t('file.terminal') }}</el-button>
|
<el-button type="primary" plain> {{ $t('file.terminal') }}</el-button>
|
||||||
<el-button type="primary" plain> {{ $t('file.shareList') }}</el-button> -->
|
<el-button type="primary" plain> {{ $t('file.shareList') }}</el-button> -->
|
||||||
@ -131,6 +131,7 @@
|
|||||||
@close="closeRename"
|
@close="closeRename"
|
||||||
></FileRename>
|
></FileRename>
|
||||||
<Upload :open="uploadPage.open" :path="uploadPage.path" @close="closeUpload"></Upload>
|
<Upload :open="uploadPage.open" :path="uploadPage.path" @close="closeUpload"></Upload>
|
||||||
|
<FileDown :open="downloadPage.open" :path="downloadPage.path" @close="closeDownload"></FileDown>
|
||||||
</el-row>
|
</el-row>
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
</template>
|
</template>
|
||||||
@ -154,6 +155,7 @@ import FileRename from './file-rename/index.vue';
|
|||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
import CodeEditor from './code-editor/index.vue';
|
import CodeEditor from './code-editor/index.vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
import FileDown from './file-down/index.vue';
|
||||||
|
|
||||||
let data = ref();
|
let data = ref();
|
||||||
let selects = ref<any>([]);
|
let selects = ref<any>([]);
|
||||||
@ -172,6 +174,7 @@ const editorPage = reactive({ open: false, content: '', loading: false });
|
|||||||
const codeReq = reactive({ path: '', expand: false });
|
const codeReq = reactive({ path: '', expand: false });
|
||||||
const uploadPage = reactive({ open: false, path: '' });
|
const uploadPage = reactive({ open: false, path: '' });
|
||||||
const renamePage = reactive({ open: false, path: '', oldName: '' });
|
const renamePage = reactive({ open: false, path: '', oldName: '' });
|
||||||
|
const downloadPage = reactive({ open: false, path: '' });
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
children: 'children',
|
children: 'children',
|
||||||
@ -346,6 +349,16 @@ const closeUpload = () => {
|
|||||||
search(req);
|
search(req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openDownload = () => {
|
||||||
|
downloadPage.open = true;
|
||||||
|
downloadPage.path = req.path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeDownload = () => {
|
||||||
|
downloadPage.open = false;
|
||||||
|
search(req);
|
||||||
|
};
|
||||||
|
|
||||||
const openRename = (item: File.File) => {
|
const openRename = (item: File.File) => {
|
||||||
renamePage.open = true;
|
renamePage.open = true;
|
||||||
renamePage.path = req.path;
|
renamePage.path = req.path;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user