mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 14:57:16 +08:00
feat: add file overwrite option to upload modal and display upload errors (#1322)
* Initial plan * feat: add file overwrite upload option and display error messages Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * fix: lint --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> Co-authored-by: 耗子 <haozi@loli.email>
This commit is contained in:
@@ -73,6 +73,7 @@ type ChunkUploadStart struct {
|
||||
FileName string `json:"file_name" validate:"required"` // 文件名
|
||||
FileHash string `json:"file_hash" validate:"required|len:64"` // 文件SHA256
|
||||
ChunkCount int `json:"chunk_count" validate:"required|min:1"` // 分块总数
|
||||
Force bool `json:"force"` // 是否覆盖已存在文件
|
||||
}
|
||||
|
||||
// ChunkUploadFinish 分块上传完成请求
|
||||
@@ -81,4 +82,5 @@ type ChunkUploadFinish struct {
|
||||
FileName string `json:"file_name" validate:"required"` // 文件名
|
||||
FileHash string `json:"file_hash" validate:"required|len:64"` // 文件SHA256
|
||||
ChunkCount int `json:"chunk_count" validate:"required|min:1"` // 分块总数
|
||||
Force bool `json:"force"` // 是否覆盖已存在文件
|
||||
}
|
||||
|
||||
@@ -155,12 +155,13 @@ func (s *FileService) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
path := r.FormValue("path")
|
||||
force := r.FormValue("force") == "true"
|
||||
_, handler, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, s.t.Get("upload file error: %v", err))
|
||||
return
|
||||
}
|
||||
if io.Exists(path) {
|
||||
if io.Exists(path) && !force {
|
||||
Error(w, http.StatusForbidden, s.t.Get("target path %s already exists", path))
|
||||
return
|
||||
}
|
||||
@@ -565,7 +566,7 @@ func (s *FileService) ChunkUploadStart(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(req.Path, req.FileName)
|
||||
if io.Exists(targetPath) {
|
||||
if io.Exists(targetPath) && !req.Force {
|
||||
Error(w, http.StatusForbidden, s.t.Get("target path %s already exists", targetPath))
|
||||
return
|
||||
}
|
||||
@@ -680,7 +681,7 @@ func (s *FileService) ChunkUploadFinish(w http.ResponseWriter, r *http.Request)
|
||||
targetPath := filepath.Join(req.Path, req.FileName)
|
||||
|
||||
// 检查目标文件是否已存在
|
||||
if io.Exists(targetPath) {
|
||||
if io.Exists(targetPath) && !req.Force {
|
||||
Error(w, http.StatusForbidden, s.t.Get("target path %s already exists", targetPath))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ export default {
|
||||
// 删除文件
|
||||
delete: (path: string): any => http.Post('/file/delete', { path }),
|
||||
// 上传文件
|
||||
upload: (formData: FormData): any =>
|
||||
http.Post('/file/upload', formData, { meta: { noAlert: true } }),
|
||||
upload: (formData: FormData): any => http.Post('/file/upload', formData),
|
||||
// 检查文件是否存在
|
||||
exist: (paths: string[]): any => http.Post('/file/exist', paths),
|
||||
// 移动文件
|
||||
@@ -48,15 +47,16 @@ export default {
|
||||
file_name: string
|
||||
file_hash: string
|
||||
chunk_count: number
|
||||
force?: boolean
|
||||
}): any => http.Post('/file/chunk/start', data),
|
||||
// 上传分块
|
||||
chunkUpload: (formData: FormData): any =>
|
||||
http.Post('/file/chunk/upload', formData, { meta: { noAlert: true } }),
|
||||
chunkUpload: (formData: FormData): any => http.Post('/file/chunk/upload', formData),
|
||||
// 完成分块上传
|
||||
chunkFinish: (data: {
|
||||
path: string
|
||||
file_name: string
|
||||
file_hash: string
|
||||
chunk_count: number
|
||||
force?: boolean
|
||||
}): any => http.Post('/file/chunk/finish', data)
|
||||
}
|
||||
|
||||
@@ -4618,6 +4618,10 @@ msgstr "There are items with the same name. Do you want to overwrite?"
|
||||
msgid "Overwrite"
|
||||
msgstr "Overwrite"
|
||||
|
||||
#: src/views/file/UploadModal.vue
|
||||
msgid "Overwrite existing files"
|
||||
msgstr "Overwrite existing files"
|
||||
|
||||
#: src/views/file/ListView.vue:744 src/views/file/ListView.vue:763
|
||||
msgid "Renamed %{ source } to %{ target } successfully"
|
||||
msgstr "Renamed %{ source } to %{ target } successfully"
|
||||
|
||||
@@ -4705,6 +4705,10 @@ msgstr ""
|
||||
msgid "Overwrite"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/file/UploadModal.vue
|
||||
msgid "Overwrite existing files"
|
||||
msgstr ""
|
||||
|
||||
#: src/views/file/ListView.vue:744
|
||||
#: src/views/file/ListView.vue:763
|
||||
msgid "Renamed %{ source } to %{ target } successfully"
|
||||
|
||||
@@ -4395,6 +4395,10 @@ msgstr "存在同名项目。您要覆盖吗?"
|
||||
msgid "Overwrite"
|
||||
msgstr "覆盖"
|
||||
|
||||
#: src/views/file/UploadModal.vue
|
||||
msgid "Overwrite existing files"
|
||||
msgstr "覆盖已存在的文件"
|
||||
|
||||
#: src/views/file/ListView.vue:744 src/views/file/ListView.vue:763
|
||||
msgid "Renamed %{ source } to %{ target } successfully"
|
||||
msgstr "成功将 %{ source } 重命名为 %{ target }"
|
||||
|
||||
@@ -4396,6 +4396,10 @@ msgstr "存在同名項目。您要覆蓋嗎?"
|
||||
msgid "Overwrite"
|
||||
msgstr "覆蓋"
|
||||
|
||||
#: src/views/file/UploadModal.vue
|
||||
msgid "Overwrite existing files"
|
||||
msgstr "覆蓋已存在的檔案"
|
||||
|
||||
#: src/views/file/ListView.vue:744 src/views/file/ListView.vue:763
|
||||
msgid "Renamed %{ source } to %{ target } successfully"
|
||||
msgstr "成功將 %{ source } 重命名為 %{ target }"
|
||||
|
||||
@@ -17,6 +17,9 @@ const props = defineProps<{
|
||||
const upload = ref<UploadInst | null>(null)
|
||||
const fileList = ref<UploadFileInfo[]>([])
|
||||
|
||||
// 覆盖上传选项
|
||||
const forceOverwrite = ref(false)
|
||||
|
||||
// 文件数量阈值,超过此数量需要二次确认
|
||||
const FILE_COUNT_THRESHOLD = 100
|
||||
// 大文件阈值,超过此大小使用分块上传 (100MB)
|
||||
@@ -203,7 +206,8 @@ const chunkedUpload = async (
|
||||
path: path.value,
|
||||
file_name: file.name,
|
||||
file_hash: fileHash,
|
||||
chunk_count: chunkCount
|
||||
chunk_count: chunkCount,
|
||||
force: forceOverwrite.value
|
||||
})
|
||||
task.activeRequests.push(startMethod)
|
||||
const startRes = await startMethod
|
||||
@@ -287,7 +291,8 @@ const chunkedUpload = async (
|
||||
path: path.value,
|
||||
file_name: file.name,
|
||||
file_hash: fileHash,
|
||||
chunk_count: chunkCount
|
||||
chunk_count: chunkCount,
|
||||
force: forceOverwrite.value
|
||||
})
|
||||
task.activeRequests.push(finishMethod)
|
||||
await finishMethod
|
||||
@@ -390,6 +395,7 @@ const uploadRequest = ({ file, onFinish, onError, onProgress }: UploadCustomRequ
|
||||
const formData = new FormData()
|
||||
formData.append('path', `${path.value}/${file.name}`)
|
||||
formData.append('file', fileObj)
|
||||
formData.append('force', forceOverwrite.value.toString())
|
||||
|
||||
const method = api.upload(formData)
|
||||
task.activeRequests.push(method)
|
||||
@@ -463,6 +469,10 @@ const handleChange = (data: { fileList: UploadFileInfo[] }) => {
|
||||
:segmented="false"
|
||||
>
|
||||
<n-flex vertical>
|
||||
<!-- 覆盖上传选项 -->
|
||||
<n-checkbox v-model:checked="forceOverwrite">
|
||||
{{ $gettext('Overwrite existing files') }}
|
||||
</n-checkbox>
|
||||
<!-- 上传速度显示 -->
|
||||
<n-flex
|
||||
v-for="[key, progress] in uploadProgressMap"
|
||||
|
||||
Reference in New Issue
Block a user