mirror of
https://github.com/krau/SaveAny-Bot.git
synced 2026-09-03 22:59:58 +08:00
fix(tdler): validate resume state against the part file
This commit is contained in:
@@ -21,9 +21,11 @@ import (
|
||||
// upload).
|
||||
func (t *Task) downloadToCache(ctx context.Context, elem *TaskElement) error {
|
||||
logger := log.FromContext(ctx).WithPrefix(fmt.Sprintf("file[%s]", elem.File.Name()))
|
||||
if stat, err := os.Stat(elem.localPath); err == nil && stat.Size() == elem.File.Size() {
|
||||
logger.Info("Cache file already complete, skipping download")
|
||||
return nil
|
||||
if elem.File.Size() > 0 {
|
||||
if stat, err := os.Stat(elem.localPath); err == nil && stat.Size() == elem.File.Size() {
|
||||
logger.Info("Cache file already complete, skipping download")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
onProgress := t.downloadCallback(ctx, elem)
|
||||
if elem.File.Size() <= 0 {
|
||||
@@ -40,7 +42,8 @@ func (t *Task) downloadToCache(ctx context.Context, elem *TaskElement) error {
|
||||
return nil
|
||||
}
|
||||
partPath := elem.localPath + ".part"
|
||||
localFile, err := fsutil.CreateFile(partPath)
|
||||
// 不截断已存在的 .part: 位图标记的已完成块依赖既有字节。
|
||||
localFile, err := os.OpenFile(partPath, os.O_CREATE|os.O_RDWR, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create local file: %w", err)
|
||||
}
|
||||
@@ -67,8 +70,9 @@ func (t *Task) downloadToCache(ctx context.Context, elem *TaskElement) error {
|
||||
if err := os.Rename(partPath, elem.localPath); err != nil {
|
||||
return fmt.Errorf("failed to finalize download: %w", err)
|
||||
}
|
||||
// 清理位图是尽力而为: 下载已完成, 清理失败不应使任务失败。
|
||||
if err := tdler.RemoveResumeState(tdler.ResumeStatePath(partPath)); err != nil {
|
||||
return fmt.Errorf("failed to remove resume state: %w", err)
|
||||
logger.Warnf("Failed to remove resume state: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -366,17 +366,12 @@ func (t *Task) processElement(ctx context.Context, elem TaskElement) error {
|
||||
return nil
|
||||
}
|
||||
logger.Info("Starting file download")
|
||||
localFile, err := fsutil.CreateFile(elem.localPath)
|
||||
if err != nil {
|
||||
t.markItemFailed(elem.ID, FailureStageCache, err)
|
||||
t.notifyStateChange(ctx)
|
||||
return fmt.Errorf("failed to create local file: %w", err)
|
||||
}
|
||||
// 不预创建缓存文件: 预创建会截断上次运行保留的完整缓存, 使复用失效。
|
||||
success := false
|
||||
defer func() {
|
||||
if success {
|
||||
if err := localFile.CloseAndRemove(); err != nil {
|
||||
logger.Errorf("Failed to close local file: %v", err)
|
||||
if err := os.Remove(elem.localPath); err != nil {
|
||||
logger.Errorf("Failed to remove cache file: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -392,8 +387,7 @@ func (t *Task) processElement(ctx context.Context, elem TaskElement) error {
|
||||
elem.Path = elem.Path + ext
|
||||
}
|
||||
}
|
||||
var fileStat os.FileInfo
|
||||
fileStat, err = os.Stat(elem.localPath)
|
||||
fileStat, err := os.Stat(elem.localPath)
|
||||
if err != nil {
|
||||
t.markItemFailed(elem.ID, FailureStageCache, err)
|
||||
t.notifyStateChange(ctx)
|
||||
|
||||
@@ -3,9 +3,9 @@ package tfile
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/charmbracelet/log"
|
||||
"os"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/krau/SaveAny-Bot/common/tdler"
|
||||
"github.com/krau/SaveAny-Bot/common/utils/dlutil"
|
||||
"github.com/krau/SaveAny-Bot/common/utils/fsutil"
|
||||
@@ -17,9 +17,11 @@ import (
|
||||
// file (e.g. when the previous run was interrupted during upload).
|
||||
func (t *Task) download(ctx context.Context) error {
|
||||
logger := log.FromContext(ctx).WithPrefix(fmt.Sprintf("file[%s]", t.File.Name()))
|
||||
if stat, err := os.Stat(t.localPath); err == nil && stat.Size() == t.File.Size() {
|
||||
logger.Info("Cache file already complete, skipping download")
|
||||
return nil
|
||||
if t.File.Size() > 0 {
|
||||
if stat, err := os.Stat(t.localPath); err == nil && stat.Size() == t.File.Size() {
|
||||
logger.Info("Cache file already complete, skipping download")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if t.File.Size() <= 0 {
|
||||
// Unknown size (e.g. photos) cannot be resumed; use the plain downloader.
|
||||
@@ -36,7 +38,8 @@ func (t *Task) download(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
partPath := t.localPath + ".part"
|
||||
localFile, err := fsutil.CreateFile(partPath)
|
||||
// 不截断已存在的 .part: 位图标记的已完成块依赖既有字节。
|
||||
localFile, err := os.OpenFile(partPath, os.O_CREATE|os.O_RDWR, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create local file: %w", err)
|
||||
}
|
||||
@@ -63,8 +66,9 @@ func (t *Task) download(ctx context.Context) error {
|
||||
if err := os.Rename(partPath, t.localPath); err != nil {
|
||||
return fmt.Errorf("failed to finalize download: %w", err)
|
||||
}
|
||||
// 清理位图是尽力而为: 下载已完成, 清理失败不应使任务失败。
|
||||
if err := tdler.RemoveResumeState(tdler.ResumeStatePath(partPath)); err != nil {
|
||||
return fmt.Errorf("failed to remove resume state: %w", err)
|
||||
logger.Warnf("Failed to remove resume state: %v", err)
|
||||
}
|
||||
logger.Info("File downloaded successfully")
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user