重构进度显示
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.22.5-alpine AS builder
|
FROM golang:1.23-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
module ghproxy
|
module ghproxy
|
||||||
|
|
||||||
go 1.22.5
|
go 1.23.0
|
||||||
|
|
||||||
|
toolchain go1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.1
|
||||||
|
golang.org/x/sync v0.14.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
|||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
|
|||||||
@@ -222,6 +222,13 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.total-progress {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: var(--inputcolor);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
background-color: #39c5bb;
|
background-color: #39c5bb;
|
||||||
@@ -231,8 +238,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.progress-text {
|
.progress-text {
|
||||||
margin-top: 5px;
|
margin-top: 10px;
|
||||||
font-size: 14px;
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-progress {
|
.image-progress {
|
||||||
@@ -328,10 +337,12 @@
|
|||||||
|
|
||||||
<div class="progress-container" id="progressContainer">
|
<div class="progress-container" id="progressContainer">
|
||||||
<h4>整体进度</h4>
|
<h4>整体进度</h4>
|
||||||
<div class="progress-bar-container">
|
<div class="total-progress">
|
||||||
<div class="progress-bar" id="totalProgressBar"></div>
|
<div class="progress-bar-container">
|
||||||
|
<div class="progress-bar" id="totalProgressBar"></div>
|
||||||
|
</div>
|
||||||
|
<div class="progress-text" id="totalProgressText">0%</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress-text" id="totalProgressText">0%</div>
|
|
||||||
|
|
||||||
<div class="image-progress" id="imageProgressList">
|
<div class="image-progress" id="imageProgressList">
|
||||||
<!-- Image progress items will be added here -->
|
<!-- Image progress items will be added here -->
|
||||||
@@ -425,6 +436,10 @@
|
|||||||
currentTaskId = data.taskId;
|
currentTaskId = data.taskId;
|
||||||
showProgressUI();
|
showProgressUI();
|
||||||
connectWebSocket(currentTaskId);
|
connectWebSocket(currentTaskId);
|
||||||
|
|
||||||
|
// 显示初始任务总数
|
||||||
|
const totalCount = data.totalCount || images.length;
|
||||||
|
totalProgressText.textContent = `0/${totalCount} - 0%`;
|
||||||
} else {
|
} else {
|
||||||
showToast('下载任务创建失败');
|
showToast('下载任务创建失败');
|
||||||
}
|
}
|
||||||
@@ -441,6 +456,11 @@
|
|||||||
imageInput.disabled = true;
|
imageInput.disabled = true;
|
||||||
platformInput.disabled = true;
|
platformInput.disabled = true;
|
||||||
|
|
||||||
|
// 设置初始总进度显示
|
||||||
|
const totalCount = images.length;
|
||||||
|
totalProgressBar.style.width = '0%';
|
||||||
|
totalProgressText.textContent = `0/${totalCount} - 0%`;
|
||||||
|
|
||||||
// 初始化每个镜像的进度条
|
// 初始化每个镜像的进度条
|
||||||
imageProgressList.innerHTML = '';
|
imageProgressList.innerHTML = '';
|
||||||
images.forEach(image => {
|
images.forEach(image => {
|
||||||
@@ -529,8 +549,9 @@
|
|||||||
// 更新总体进度
|
// 更新总体进度
|
||||||
function updateProgress(data) {
|
function updateProgress(data) {
|
||||||
// 更新总进度
|
// 更新总进度
|
||||||
totalProgressBar.style.width = `${data.totalProgress}%`;
|
const progressPercent = data.totalCount > 0 ? (data.completedCount / data.totalCount) * 100 : 0;
|
||||||
totalProgressText.textContent = `${Math.round(data.totalProgress)}%`;
|
totalProgressBar.style.width = `${progressPercent}%`;
|
||||||
|
totalProgressText.textContent = `${data.completedCount}/${data.totalCount} - ${Math.round(progressPercent)}%`;
|
||||||
|
|
||||||
// 更新各个镜像的进度
|
// 更新各个镜像的进度
|
||||||
data.images.forEach(imgData => {
|
data.images.forEach(imgData => {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -47,7 +46,8 @@ type ImageTask struct {
|
|||||||
type DownloadTask struct {
|
type DownloadTask struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Images []*ImageTask `json:"images"`
|
Images []*ImageTask `json:"images"`
|
||||||
TotalProgress float64 `json:"totalProgress"`
|
CompletedCount int `json:"completedCount"` // 已完成任务数
|
||||||
|
TotalCount int `json:"totalCount"` // 总任务数
|
||||||
Status TaskStatus `json:"status"`
|
Status TaskStatus `json:"status"`
|
||||||
OutputFile string `json:"-"` // 最终输出文件
|
OutputFile string `json:"-"` // 最终输出文件
|
||||||
TempDir string `json:"-"` // 临时目录
|
TempDir string `json:"-"` // 临时目录
|
||||||
@@ -189,7 +189,8 @@ func getTaskStatus(c *gin.Context) {
|
|||||||
// 创建任务状态副本以避免序列化过程中的锁
|
// 创建任务状态副本以避免序列化过程中的锁
|
||||||
taskCopy := &DownloadTask{
|
taskCopy := &DownloadTask{
|
||||||
ID: task.ID,
|
ID: task.ID,
|
||||||
TotalProgress: 0,
|
CompletedCount: 0,
|
||||||
|
TotalCount: len(task.Images),
|
||||||
Status: TaskStatus(""),
|
Status: TaskStatus(""),
|
||||||
Images: nil,
|
Images: nil,
|
||||||
}
|
}
|
||||||
@@ -200,7 +201,7 @@ func getTaskStatus(c *gin.Context) {
|
|||||||
task.StatusLock.RUnlock()
|
task.StatusLock.RUnlock()
|
||||||
|
|
||||||
task.ProgressLock.RLock()
|
task.ProgressLock.RLock()
|
||||||
taskCopy.TotalProgress = task.TotalProgress
|
taskCopy.CompletedCount = task.CompletedCount
|
||||||
task.ProgressLock.RUnlock()
|
task.ProgressLock.RUnlock()
|
||||||
|
|
||||||
// 复制镜像信息
|
// 复制镜像信息
|
||||||
@@ -250,21 +251,42 @@ func initTask(task *DownloadTask) {
|
|||||||
imgTask := task.Images[update.ImageIndex]
|
imgTask := task.Images[update.ImageIndex]
|
||||||
task.ImageLock.RUnlock()
|
task.ImageLock.RUnlock()
|
||||||
|
|
||||||
|
statusChanged := false
|
||||||
|
prevStatus := ""
|
||||||
|
|
||||||
// 更新镜像进度和状态
|
// 更新镜像进度和状态
|
||||||
imgTask.lock.Lock()
|
imgTask.lock.Lock()
|
||||||
if update.Progress > 0 {
|
if update.Progress > 0 {
|
||||||
imgTask.Progress = update.Progress
|
imgTask.Progress = update.Progress
|
||||||
}
|
}
|
||||||
if update.Status != "" {
|
if update.Status != "" && update.Status != imgTask.Status {
|
||||||
|
prevStatus = imgTask.Status
|
||||||
imgTask.Status = update.Status
|
imgTask.Status = update.Status
|
||||||
|
statusChanged = true
|
||||||
}
|
}
|
||||||
if update.Error != "" {
|
if update.Error != "" {
|
||||||
imgTask.Error = update.Error
|
imgTask.Error = update.Error
|
||||||
}
|
}
|
||||||
imgTask.lock.Unlock()
|
imgTask.lock.Unlock()
|
||||||
|
|
||||||
// 更新总进度
|
// 检查状态变化并更新完成计数
|
||||||
updateTaskTotalProgress(task)
|
if statusChanged {
|
||||||
|
task.ProgressLock.Lock()
|
||||||
|
// 如果之前不是Completed,现在是Completed,增加计数
|
||||||
|
if prevStatus != string(StatusCompleted) && update.Status == string(StatusCompleted) {
|
||||||
|
task.CompletedCount++
|
||||||
|
fmt.Printf("任务 %s: 镜像 %d 完成,当前完成数: %d/%d\n",
|
||||||
|
task.ID, update.ImageIndex, task.CompletedCount, task.TotalCount)
|
||||||
|
}
|
||||||
|
// 如果之前是Completed,现在不是,减少计数
|
||||||
|
if prevStatus == string(StatusCompleted) && update.Status != string(StatusCompleted) {
|
||||||
|
task.CompletedCount--
|
||||||
|
if task.CompletedCount < 0 {
|
||||||
|
task.CompletedCount = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
task.ProgressLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// 发送更新到客户端
|
// 发送更新到客户端
|
||||||
sendTaskUpdate(task)
|
sendTaskUpdate(task)
|
||||||
@@ -289,30 +311,35 @@ func sendProgressUpdate(task *DownloadTask, index int, progress float64, status
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新总进度 - 不需要外部锁
|
// 更新总进度 - 重新计算已完成任务数
|
||||||
func updateTaskTotalProgress(task *DownloadTask) {
|
func updateTaskTotalProgress(task *DownloadTask) {
|
||||||
task.ProgressLock.Lock()
|
task.ProgressLock.Lock()
|
||||||
defer task.ProgressLock.Unlock()
|
defer task.ProgressLock.Unlock()
|
||||||
|
|
||||||
totalProgress := 0.0
|
completedCount := 0
|
||||||
|
|
||||||
task.ImageLock.RLock()
|
task.ImageLock.RLock()
|
||||||
imageCount := len(task.Images)
|
totalCount := len(task.Images)
|
||||||
task.ImageLock.RUnlock()
|
task.ImageLock.RUnlock()
|
||||||
|
|
||||||
if imageCount == 0 {
|
if totalCount == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
task.ImageLock.RLock()
|
task.ImageLock.RLock()
|
||||||
for _, img := range task.Images {
|
for _, img := range task.Images {
|
||||||
img.lock.Lock()
|
img.lock.Lock()
|
||||||
totalProgress += img.Progress
|
if img.Status == string(StatusCompleted) {
|
||||||
|
completedCount++
|
||||||
|
}
|
||||||
img.lock.Unlock()
|
img.lock.Unlock()
|
||||||
}
|
}
|
||||||
task.ImageLock.RUnlock()
|
task.ImageLock.RUnlock()
|
||||||
|
|
||||||
task.TotalProgress = totalProgress / float64(imageCount)
|
task.CompletedCount = completedCount
|
||||||
|
task.TotalCount = totalCount
|
||||||
|
|
||||||
|
fmt.Printf("任务 %s: 进度更新 %d/%d 已完成\n", task.ID, completedCount, totalCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理下载请求
|
// 处理下载请求
|
||||||
@@ -351,7 +378,8 @@ func handleDownload(c *gin.Context) {
|
|||||||
task := &DownloadTask{
|
task := &DownloadTask{
|
||||||
ID: taskID,
|
ID: taskID,
|
||||||
Images: imageTasks,
|
Images: imageTasks,
|
||||||
TotalProgress: 0,
|
CompletedCount: 0,
|
||||||
|
TotalCount: len(imageTasks),
|
||||||
Status: StatusPending,
|
Status: StatusPending,
|
||||||
TempDir: tempDir,
|
TempDir: tempDir,
|
||||||
}
|
}
|
||||||
@@ -374,6 +402,7 @@ func handleDownload(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"taskId": taskID,
|
"taskId": taskID,
|
||||||
"status": "started",
|
"status": "started",
|
||||||
|
"totalCount": len(imageTasks),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,6 +413,16 @@ func processDownloadTask(task *DownloadTask, platform string) {
|
|||||||
task.Status = StatusRunning
|
task.Status = StatusRunning
|
||||||
task.StatusLock.Unlock()
|
task.StatusLock.Unlock()
|
||||||
|
|
||||||
|
// 初始化总任务数
|
||||||
|
task.ImageLock.RLock()
|
||||||
|
imageCount := len(task.Images)
|
||||||
|
task.ImageLock.RUnlock()
|
||||||
|
|
||||||
|
task.ProgressLock.Lock()
|
||||||
|
task.TotalCount = imageCount
|
||||||
|
task.CompletedCount = 0
|
||||||
|
task.ProgressLock.Unlock()
|
||||||
|
|
||||||
// 通知客户端任务已开始
|
// 通知客户端任务已开始
|
||||||
sendTaskUpdate(task)
|
sendTaskUpdate(task)
|
||||||
|
|
||||||
@@ -395,7 +434,7 @@ func processDownloadTask(task *DownloadTask, platform string) {
|
|||||||
|
|
||||||
// 启动并发下载
|
// 启动并发下载
|
||||||
task.ImageLock.RLock()
|
task.ImageLock.RLock()
|
||||||
imageCount := len(task.Images)
|
imageCount = len(task.Images)
|
||||||
task.ImageLock.RUnlock()
|
task.ImageLock.RUnlock()
|
||||||
|
|
||||||
// 创建工作池限制并发数
|
// 创建工作池限制并发数
|
||||||
@@ -428,6 +467,9 @@ func processDownloadTask(task *DownloadTask, platform string) {
|
|||||||
// 等待所有下载完成
|
// 等待所有下载完成
|
||||||
err := g.Wait()
|
err := g.Wait()
|
||||||
|
|
||||||
|
// 再次计算已完成任务数,确保正确
|
||||||
|
updateTaskTotalProgress(task)
|
||||||
|
|
||||||
// 检查是否有错误发生
|
// 检查是否有错误发生
|
||||||
if err != nil {
|
if err != nil {
|
||||||
task.StatusLock.Lock()
|
task.StatusLock.Lock()
|
||||||
@@ -496,15 +538,20 @@ func processDownloadTask(task *DownloadTask, platform string) {
|
|||||||
task.OutputFile = finalFilePath
|
task.OutputFile = finalFilePath
|
||||||
task.Status = StatusCompleted
|
task.Status = StatusCompleted
|
||||||
|
|
||||||
// 设置总进度为100%
|
// 设置完成计数为总任务数
|
||||||
task.ProgressLock.Lock()
|
task.ProgressLock.Lock()
|
||||||
task.TotalProgress = 100
|
task.CompletedCount = task.TotalCount
|
||||||
task.ProgressLock.Unlock()
|
task.ProgressLock.Unlock()
|
||||||
|
|
||||||
task.StatusLock.Unlock()
|
task.StatusLock.Unlock()
|
||||||
|
|
||||||
// 发送最终状态更新
|
// 发送最终状态更新
|
||||||
sendTaskUpdate(task)
|
sendTaskUpdate(task)
|
||||||
|
|
||||||
|
// 确保所有进度都达到100%
|
||||||
|
ensureTaskCompletion(task)
|
||||||
|
|
||||||
|
fmt.Printf("任务 %s 全部完成: %d/%d\n", task.ID, task.CompletedCount, task.TotalCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载单个镜像(带上下文控制)
|
// 下载单个镜像(带上下文控制)
|
||||||
@@ -575,7 +622,6 @@ func downloadImageWithContext(ctx context.Context, task *DownloadTask, index int
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用进度通道传递进度信息
|
// 使用进度通道传递进度信息
|
||||||
progressChan := make(chan float64, 10)
|
|
||||||
outputChan := make(chan string, 20)
|
outputChan := make(chan string, 20)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
@@ -606,7 +652,11 @@ func downloadImageWithContext(ctx context.Context, task *DownloadTask, index int
|
|||||||
return
|
return
|
||||||
|
|
||||||
case <-done:
|
case <-done:
|
||||||
// 命令完成
|
// 命令完成,强制更新到100%
|
||||||
|
if lastProgress < 100 {
|
||||||
|
fmt.Printf("镜像 %s 下载完成,强制更新进度到100%%\n", imgTask.Image)
|
||||||
|
sendProgressUpdate(task, index, 100, string(StatusCompleted), "")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
case output := <-outputChan:
|
case output := <-outputChan:
|
||||||
@@ -654,6 +704,14 @@ func downloadImageWithContext(ctx context.Context, task *DownloadTask, index int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果发现完成标记,立即更新到100%
|
||||||
|
if checkForCompletionMarkers(output) {
|
||||||
|
fmt.Printf("镜像 %s 检测到完成标记\n", imgTask.Image)
|
||||||
|
lastProgress = 100
|
||||||
|
sendProgressUpdate(task, index, 100, string(StatusCompleted), "")
|
||||||
|
stagnantTime = 0
|
||||||
|
}
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
// 如果进度长时间无变化,缓慢增加
|
// 如果进度长时间无变化,缓慢增加
|
||||||
stagnantTime += 100 // 100ms
|
stagnantTime += 100 // 100ms
|
||||||
@@ -716,7 +774,7 @@ func downloadImageWithContext(ctx context.Context, task *DownloadTask, index int
|
|||||||
return fmt.Errorf(errMsg)
|
return fmt.Errorf(errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新状态为已完成
|
// 确保更新状态为已完成,进度为100%
|
||||||
sendProgressUpdate(task, index, 100, string(StatusCompleted), "")
|
sendProgressUpdate(task, index, 100, string(StatusCompleted), "")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -796,7 +854,8 @@ func sendTaskUpdate(task *DownloadTask) {
|
|||||||
// 复制任务状态避免序列化时锁定
|
// 复制任务状态避免序列化时锁定
|
||||||
taskCopy := &DownloadTask{
|
taskCopy := &DownloadTask{
|
||||||
ID: task.ID,
|
ID: task.ID,
|
||||||
TotalProgress: 0,
|
CompletedCount: 0,
|
||||||
|
TotalCount: len(task.Images),
|
||||||
Status: TaskStatus(""),
|
Status: TaskStatus(""),
|
||||||
Images: nil,
|
Images: nil,
|
||||||
}
|
}
|
||||||
@@ -807,7 +866,7 @@ func sendTaskUpdate(task *DownloadTask) {
|
|||||||
task.StatusLock.RUnlock()
|
task.StatusLock.RUnlock()
|
||||||
|
|
||||||
task.ProgressLock.RLock()
|
task.ProgressLock.RLock()
|
||||||
taskCopy.TotalProgress = task.TotalProgress
|
taskCopy.CompletedCount = task.CompletedCount
|
||||||
task.ProgressLock.RUnlock()
|
task.ProgressLock.RUnlock()
|
||||||
|
|
||||||
// 复制镜像信息
|
// 复制镜像信息
|
||||||
@@ -879,6 +938,16 @@ func serveFile(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 确保任务状态为已完成,并且所有进度都是100%
|
||||||
|
task.StatusLock.RLock()
|
||||||
|
isCompleted := task.Status == StatusCompleted
|
||||||
|
task.StatusLock.RUnlock()
|
||||||
|
|
||||||
|
if isCompleted {
|
||||||
|
// 确保所有进度达到100%
|
||||||
|
ensureTaskCompletion(task)
|
||||||
|
}
|
||||||
|
|
||||||
// 检查文件是否存在
|
// 检查文件是否存在
|
||||||
filePath := task.OutputFile
|
filePath := task.OutputFile
|
||||||
if filePath == "" || !fileExists(filePath) {
|
if filePath == "" || !fileExists(filePath) {
|
||||||
@@ -941,3 +1010,65 @@ func cleanupTempFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 完成任务处理函数,确保进度是100%
|
||||||
|
func ensureTaskCompletion(task *DownloadTask) {
|
||||||
|
// 重新检查一遍所有镜像的进度
|
||||||
|
task.ImageLock.RLock()
|
||||||
|
completedCount := 0
|
||||||
|
totalCount := len(task.Images)
|
||||||
|
|
||||||
|
for i, img := range task.Images {
|
||||||
|
img.lock.Lock()
|
||||||
|
if img.Status == string(StatusCompleted) {
|
||||||
|
// 确保进度为100%
|
||||||
|
if img.Progress < 100 {
|
||||||
|
img.Progress = 100
|
||||||
|
fmt.Printf("确保镜像 %d 进度为100%%\n", i)
|
||||||
|
}
|
||||||
|
completedCount++
|
||||||
|
}
|
||||||
|
img.lock.Unlock()
|
||||||
|
}
|
||||||
|
task.ImageLock.RUnlock()
|
||||||
|
|
||||||
|
// 更新完成计数
|
||||||
|
task.ProgressLock.Lock()
|
||||||
|
task.CompletedCount = completedCount
|
||||||
|
task.TotalCount = totalCount
|
||||||
|
task.ProgressLock.Unlock()
|
||||||
|
|
||||||
|
// 如果任务状态为已完成,但计数不匹配,修正计数
|
||||||
|
task.StatusLock.RLock()
|
||||||
|
isCompleted := task.Status == StatusCompleted
|
||||||
|
task.StatusLock.RUnlock()
|
||||||
|
|
||||||
|
if isCompleted && completedCount != totalCount {
|
||||||
|
task.ProgressLock.Lock()
|
||||||
|
task.CompletedCount = totalCount
|
||||||
|
task.ProgressLock.Unlock()
|
||||||
|
fmt.Printf("任务 %s 状态已完成,强制设置计数为 %d/%d\n", task.ID, totalCount, totalCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送最终更新
|
||||||
|
sendTaskUpdate(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理下载单个镜像的输出中的完成标记
|
||||||
|
func checkForCompletionMarkers(output string) bool {
|
||||||
|
// 已知的完成标记
|
||||||
|
completionMarkers := []string{
|
||||||
|
"Writing manifest to image destination",
|
||||||
|
"Copying config complete",
|
||||||
|
"Storing signatures",
|
||||||
|
"Writing manifest complete",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, marker := range completionMarkers {
|
||||||
|
if strings.Contains(output, marker) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user