- 新增 ClearFilesNoPrompt 配置项用于控制是否提示清空文件 - 实现临时文件目录(tmp)管理,优先处理未上传完的文件 - 添加文件复制功能,支持快速零拷贝技术 - 实现文件清空提示机制,支持用户确认操作 - 优化文件上传流程,添加进度跟踪和状态更新 - 过滤掉大小为0的文件,避免无效上传 - 修改数据结构名称提升代码可读性
This commit is contained in:
+22
-18
@@ -9,35 +9,38 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Url string `json:"url" mapstructure:"url"`
|
Url string `json:"url" mapstructure:"url"`
|
||||||
Token string `json:"token" mapstructure:"token"`
|
Token string `json:"token" mapstructure:"token"`
|
||||||
ThreadCount int `json:"thread_count" mapstructure:"thread-count"`
|
ThreadCount int `json:"thread_count" mapstructure:"thread-count"`
|
||||||
HandleFileCount int `json:"handle_file_count" mapstructure:"handle-file-count"`
|
HandleFileCount int `json:"handle_file_count" mapstructure:"handle-file-count"`
|
||||||
IsRunOnStart bool `json:"is_run_on_start" mapstructure:"is-run-on-start"`
|
IsRunOnStart bool `json:"is_run_on_start" mapstructure:"is-run-on-start"`
|
||||||
CheckDir string `json:"check_dir" mapstructure:"check-dir"`
|
CheckDir string `json:"check_dir" mapstructure:"check-dir"`
|
||||||
|
ClearFilesNoPrompt bool `json:"clear_files_no_prompt" mapstructure:"clear-files-no-prompt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var APPConfig Config
|
var APPConfig Config
|
||||||
var configMu sync.Mutex
|
var configMu sync.Mutex
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Url = "url"
|
Url = "url"
|
||||||
Token = "token"
|
Token = "token"
|
||||||
ThreadCount = "thread-count"
|
ThreadCount = "thread-count"
|
||||||
HandleFileCount = "handle-file-count"
|
HandleFileCount = "handle-file-count"
|
||||||
IsRunOnStart = "is-run-on-start"
|
IsRunOnStart = "is-run-on-start"
|
||||||
CheckDir = "check-dir"
|
CheckDir = "check-dir"
|
||||||
|
ClearFilesNoPrompt = "clear-files-no-prompt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitConfig() {
|
func InitConfig() {
|
||||||
// 设置默认配置
|
// 设置默认配置
|
||||||
defaultConfig := Config{
|
defaultConfig := Config{
|
||||||
Url: "http://127.0.0.1:8080",
|
Url: "http://127.0.0.1:8080",
|
||||||
Token: "",
|
Token: "",
|
||||||
ThreadCount: 10,
|
ThreadCount: 10,
|
||||||
HandleFileCount: 25,
|
HandleFileCount: 25,
|
||||||
IsRunOnStart: false,
|
IsRunOnStart: false,
|
||||||
CheckDir: "",
|
CheckDir: "",
|
||||||
|
ClearFilesNoPrompt: false,
|
||||||
}
|
}
|
||||||
viper.SetDefault(Url, defaultConfig.Url)
|
viper.SetDefault(Url, defaultConfig.Url)
|
||||||
viper.SetDefault(Token, defaultConfig.Token)
|
viper.SetDefault(Token, defaultConfig.Token)
|
||||||
@@ -45,6 +48,7 @@ func InitConfig() {
|
|||||||
viper.SetDefault(HandleFileCount, defaultConfig.HandleFileCount)
|
viper.SetDefault(HandleFileCount, defaultConfig.HandleFileCount)
|
||||||
viper.SetDefault(IsRunOnStart, defaultConfig.IsRunOnStart)
|
viper.SetDefault(IsRunOnStart, defaultConfig.IsRunOnStart)
|
||||||
viper.SetDefault(CheckDir, defaultConfig.CheckDir)
|
viper.SetDefault(CheckDir, defaultConfig.CheckDir)
|
||||||
|
viper.SetDefault(ClearFilesNoPrompt, defaultConfig.ClearFilesNoPrompt)
|
||||||
|
|
||||||
//设置配置文件名和路径 ./config.toml
|
//设置配置文件名和路径 ./config.toml
|
||||||
viper.AddConfigPath(".")
|
viper.AddConfigPath(".")
|
||||||
|
|||||||
+136
-34
@@ -7,6 +7,7 @@ import (
|
|||||||
"dypid-client/internal/api"
|
"dypid-client/internal/api"
|
||||||
"dypid-client/internal/config"
|
"dypid-client/internal/config"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -20,7 +21,7 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Task struct {
|
type fileInfo struct {
|
||||||
FilePath string
|
FilePath string
|
||||||
FileLines int
|
FileLines int
|
||||||
}
|
}
|
||||||
@@ -80,28 +81,92 @@ func StartUpload(ctx context.Context, logChan *chan string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func uploadData(ctx context.Context, logChan *chan string) {
|
func uploadData(ctx context.Context, logChan *chan string) {
|
||||||
// 获取检测目录
|
|
||||||
var path = "./"
|
|
||||||
if config.APPConfig.CheckDir != "" {
|
|
||||||
path = config.APPConfig.CheckDir
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取文件列表
|
|
||||||
files, err := getTxtFiles(path)
|
|
||||||
if err != nil {
|
|
||||||
AddLog(logChan, "获取文件列表失败:"+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if files == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
// 获取检测目录
|
||||||
|
var checkPath = "./"
|
||||||
|
if config.APPConfig.CheckDir != "" {
|
||||||
|
checkPath = config.APPConfig.CheckDir
|
||||||
|
}
|
||||||
|
|
||||||
|
//要上传的文件路径字符串数组
|
||||||
|
var files []string
|
||||||
|
|
||||||
|
//先检测tmp目录有没有残余文件
|
||||||
|
os.Mkdir("./tmp", os.ModePerm)
|
||||||
|
tmpFiles, err := getTxtFiles("./tmp")
|
||||||
|
if err != nil {
|
||||||
|
AddLog(logChan, "获取 tmp 文件列表失败:"+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
//tmp有文件,优先上传(else:tmp没文件扫描指定文件夹,并复制文件到tmp)
|
||||||
|
if tmpFiles != nil {
|
||||||
|
AddLog(logChan, "当前 tmp 目录下还有未上传完成文件,将优先上传 tmp 目录文件")
|
||||||
|
files = tmpFiles
|
||||||
|
} else {
|
||||||
|
//tmp没文件,扫描指定文件夹
|
||||||
|
f, err := getTxtFiles(checkPath)
|
||||||
|
if err != nil {
|
||||||
|
AddLog(logChan, "获取文件列表失败:"+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//指定文件夹没文件,退出函数
|
||||||
|
if f == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//是否向用户提示清空文件,并复制文件到tmp
|
||||||
|
if config.APPConfig.ClearFilesNoPrompt {
|
||||||
|
//不用提示直接复制文件到tmp
|
||||||
|
for _, p := range f {
|
||||||
|
err := copyFile(p, "./tmp/"+filepath.Base(p))
|
||||||
|
if err != nil {
|
||||||
|
AddLog(logChan, "复制文件失败:"+err.Error())
|
||||||
|
} else {
|
||||||
|
files = append(files, "./tmp/"+filepath.Base(p))
|
||||||
|
err := os.Truncate(p, 0)
|
||||||
|
if err != nil {
|
||||||
|
AddLog(logChan, "清空文件失败:"+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//提示用户
|
||||||
|
wailsruntime.EventsEmit(ctx, "clear-files", f)
|
||||||
|
|
||||||
|
confirm := make(chan bool)
|
||||||
|
|
||||||
|
wailsruntime.EventsOn(ctx, "confirm-clear-files", func(optionalData ...any) {
|
||||||
|
confirm <- optionalData[0].(bool)
|
||||||
|
})
|
||||||
|
|
||||||
|
if <-confirm {
|
||||||
|
for _, p := range f {
|
||||||
|
err := copyFile(p, "./tmp/"+filepath.Base(p))
|
||||||
|
if err != nil {
|
||||||
|
AddLog(logChan, "复制文件失败:"+err.Error())
|
||||||
|
} else {
|
||||||
|
files = append(files, "./tmp/"+filepath.Base(p))
|
||||||
|
err := os.Truncate(p, 0)
|
||||||
|
if err != nil {
|
||||||
|
AddLog(logChan, "清空文件失败:"+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AddLog(logChan, "已取消上传,1分钟后再运行")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//检测到文件
|
//检测到文件
|
||||||
//统计文件行数
|
//统计文件行数
|
||||||
fileLines := make(map[string]int)
|
var fInfo = make(map[string]fileInfo)
|
||||||
|
|
||||||
AddLog(logChan, fmt.Sprintf("正在统计 %v 个文件行数", len(files)))
|
AddLog(logChan, fmt.Sprintf("正在统计 %v 个文件行数", len(files)))
|
||||||
|
|
||||||
isAllEmpty := true
|
isAllEmpty := true
|
||||||
for _, filePath := range files {
|
for _, filePath := range files {
|
||||||
select {
|
select {
|
||||||
@@ -124,8 +189,13 @@ func uploadData(ctx context.Context, logChan *chan string) {
|
|||||||
if lineCount == 0 {
|
if lineCount == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fileLines[filepath.Base(filePath)] = lineCount
|
fInfo[filepath.Base(filePath)] = fileInfo{
|
||||||
|
FilePath: filePath,
|
||||||
|
FileLines: lineCount,
|
||||||
|
}
|
||||||
|
|
||||||
isAllEmpty = false
|
isAllEmpty = false
|
||||||
|
|
||||||
AddLog(logChan, fmt.Sprintf("%s 文件行数:%v", filepath.Base(filePath), lineCount))
|
AddLog(logChan, fmt.Sprintf("%s 文件行数:%v", filepath.Base(filePath), lineCount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,20 +205,24 @@ func uploadData(ctx context.Context, logChan *chan string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//刷新文件上传进度
|
||||||
progress.Clear()
|
progress.Clear()
|
||||||
|
for fileName, info := range fInfo {
|
||||||
//添加文件上传任务参数(文件路径,文件行数)
|
progress.Store(fileName,
|
||||||
var tasks []Task
|
Progress{
|
||||||
for fileName, lines := range fileLines {
|
FileName: fileName,
|
||||||
tasks = append(tasks, Task{FilePath: path + "/" + fileName, FileLines: lines})
|
Total: info.FileLines,
|
||||||
progress.Store(fileName, Progress{FileName: fileName, Total: lines, Uploaded: 0, Percentage: 0})
|
Uploaded: 0,
|
||||||
|
Percentage: 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 errgroup 控制同时处理的文件数,并开始上传文件任务
|
// 使用 errgroup 控制同时处理的文件数,并开始上传文件任务
|
||||||
g, egctx := errgroup.WithContext(ctx)
|
g, egctx := errgroup.WithContext(ctx)
|
||||||
g.SetLimit(config.APPConfig.HandleFileCount) // 设置同时处理文件数
|
g.SetLimit(config.APPConfig.HandleFileCount) // 设置同时处理文件数
|
||||||
// 执行所有任务
|
// 执行文件上传任务参数(文件路径,文件行数)
|
||||||
for _, task := range tasks {
|
for fileName, info := range fInfo {
|
||||||
select {
|
select {
|
||||||
case <-egctx.Done():
|
case <-egctx.Done():
|
||||||
return
|
return
|
||||||
@@ -158,18 +232,18 @@ func uploadData(ctx context.Context, logChan *chan string) {
|
|||||||
case <-egctx.Done():
|
case <-egctx.Done():
|
||||||
return egctx.Err()
|
return egctx.Err()
|
||||||
default:
|
default:
|
||||||
AddLog(logChan, "正在上传文件:"+filepath.Base(task.FilePath))
|
AddLog(logChan, "正在上传文件:"+fileName)
|
||||||
|
|
||||||
processFile(egctx, logChan, task.FilePath, task.FileLines)
|
processFile(egctx, logChan, info.FilePath, info.FileLines)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-egctx.Done():
|
case <-egctx.Done():
|
||||||
return egctx.Err()
|
return egctx.Err()
|
||||||
default:
|
default:
|
||||||
//上传完成,清空文件
|
//上传完成,删除缓存文件
|
||||||
err := os.Truncate(task.FilePath, 0)
|
err := os.Remove(info.FilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
AddLog(logChan, "清空文件失败:"+err.Error())
|
AddLog(logChan, "删除缓存文件失败:"+err.Error())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -190,7 +264,33 @@ func uploadData(ctx context.Context, logChan *chan string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取目录中的所有txt文件
|
// copyFile 快速拷贝文件 src -> dst
|
||||||
|
func copyFile(src, dst string) error {
|
||||||
|
// 打开源文件
|
||||||
|
sourceFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
// 创建目标文件
|
||||||
|
destFile, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
// 核心:最快拷贝,底层使用操作系统零拷贝技术
|
||||||
|
_, err = io.Copy(destFile, sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 强制刷入磁盘,保证数据完整
|
||||||
|
return destFile.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目录中的所有txt文件(文件大小为0的不返回)
|
||||||
func getTxtFiles(dir string) (txtFiles []string, err error) {
|
func getTxtFiles(dir string) (txtFiles []string, err error) {
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -204,7 +304,9 @@ func getTxtFiles(dir string) (txtFiles []string, err error) {
|
|||||||
|
|
||||||
// 检查文件扩展名是否为.txt
|
// 检查文件扩展名是否为.txt
|
||||||
if strings.ToLower(filepath.Ext(path)) == ".txt" {
|
if strings.ToLower(filepath.Ext(path)) == ".txt" {
|
||||||
txtFiles = append(txtFiles, path)
|
if info.Size() != 0 {
|
||||||
|
txtFiles = append(txtFiles, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user