flipped-aurora/gin-vue-admin · error
文件删除失败:
Error message
文件删除失败:
What it means
This error is returned by Local.DeleteFile when os.Remove(p) fails after the file was confirmed to exist and the delete lock was acquired. It wraps the underlying OS error (permission denied, device busy, read-only filesystem, etc.) appended after the '文件删除失败: ' prefix.
Source
Thrown at server/utils/upload/local.go:100
func (l *Local) DeleteFile(ctx context.Context, key string) error {
p, err := l.localPath(ctx, key)
if err != nil {
return err
}
// 检查文件是否存在
if _, err := os.Stat(p); os.IsNotExist(err) {
return errors.New("文件不存在")
}
// 使用文件锁防止并发删除
mu.Lock()
defer mu.Unlock()
err = os.Remove(p)
if err != nil {
return errors.New("文件删除失败: " + err.Error())
}
return nil
}
// localPath 校验 key 并拼接出本地存储的绝对路径,复用 DeleteFile 中的路径穿越防护逻辑。
func (*Local) localPath(ctx context.Context, key string) (string, error) {
// 检查 key 是否为空
if key == "" {
return "", errors.New("key不能为空")
}
// 验证 key 是否包含非法字符或尝试访问存储路径之外的文件
if strings.Contains(key, "..") || strings.ContainsAny(key, `\/:*?"<>|`) {
return "", errors.New("非法的key")
}
return filepath.Join(global.GVA_CONFIG.Local.StorePath, key), nilView on GitHub (pinned to 3136500ef3)
Solutions
- Read the appended OS error and fix the cause: chmod/chown the StorePath directory so the process user has write+execute permission.
- On Windows, identify and close processes holding the file open (handle.exe / Resource Monitor), or retry after they release it.
- If storage is a mounted volume, verify it is mounted read-write (mount options, K8s volume spec).
- If it's a Stat/Remove race, retry the delete once — the second attempt will report 文件不存在 which you can treat as success.
Example fix
// before # ls -l uploads -> drwxr-xr-x root root; server runs as appuser // after # shell sudo chown appuser:appuser uploads && chmod 755 uploads
Defensive patterns
Strategy: try-catch
Validate before calling
p := filepath.Join(storePath, key)
info, err := os.Stat(p)
if err != nil {
return fmt.Errorf("cannot access file: %w", err)
}
dir := filepath.Dir(p)
if ti, err := os.Stat(dir); err != nil || ti.Mode()&0o200 == 0 {
return errors.New("store directory is not writable by this process")
} Try / catch
err := local.DeleteFile(ctx, key)
if err != nil {
if strings.HasPrefix(err.Error(), "文件删除失败") {
// inspect appended OS error; retry once on transient EBUSY, escalate on EACCES
time.Sleep(100 * time.Millisecond)
err = local.DeleteFile(ctx, key)
}
if err != nil {
return err
}
} Prevention
- Grant the server process write+execute on the store directory (chown/chmod at deploy time).
- Avoid read-only mounts for the uploads volume; assert writability in a readiness probe.
- On Windows, exclude the uploads dir from scanners that hold files open.
- Log the appended OS error verbatim to distinguish permission vs busy vs stale-handle.
When it happens
Trigger: DeleteFile called on an existing file whose removal fails: process lacks write permission on the containing directory, the file is open/locked by another process (Windows), the store lives on a read-only mount, or a race removed the file between Stat and Remove.
Common situations: Uploads directory owned by root while the server runs as a non-root user; Windows services holding files open (antivirus scanners, log shippers); Kubernetes volumes mounted read-only by mistake; NFS stale handles.
Related errors
- function os.Create() failed, err:
- 文件不存在
- function os.ReadDir() failed, err:
- 创建编译根目录失败: %w
- 删除新建文件 %s 失败: %w
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/2da7e60c045c86d7.
Report an issue: GitHub.