if the file being received already exists locally, it will be renamed
This commit is contained in:
@@ -13,11 +13,8 @@ import (
|
||||
|
||||
// WindowState 定义窗口状态
|
||||
type WindowState struct {
|
||||
Width int `mapstructure:"width"`
|
||||
Height int `mapstructure:"height"`
|
||||
X int `mapstructure:"x"`
|
||||
Y int `mapstructure:"y"`
|
||||
Maximised bool `mapstructure:"maximised"`
|
||||
Width int `mapstructure:"width"`
|
||||
Height int `mapstructure:"height"`
|
||||
}
|
||||
|
||||
var Version = "next"
|
||||
@@ -50,14 +47,6 @@ type Config struct {
|
||||
data configData
|
||||
}
|
||||
|
||||
// 默认窗口配置
|
||||
var defaultWindowState = WindowState{
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
X: -1,
|
||||
Y: -1,
|
||||
}
|
||||
|
||||
func GetConfigDir() string {
|
||||
configPath, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
@@ -75,7 +64,7 @@ func GetUserHomeDir() string {
|
||||
}
|
||||
|
||||
// New 读取配置
|
||||
func Load() *Config {
|
||||
func Load(defaultState WindowState) *Config {
|
||||
v := viper.New()
|
||||
configDir := GetConfigDir()
|
||||
err := os.MkdirAll(configDir, 0755)
|
||||
@@ -86,7 +75,7 @@ func Load() *Config {
|
||||
|
||||
// 设置默认值
|
||||
defaultSavePath := filepath.Join(GetUserHomeDir(), "Downloads")
|
||||
v.SetDefault("window_state", defaultWindowState)
|
||||
v.SetDefault("window_state", defaultState)
|
||||
v.SetDefault("save_path", defaultSavePath)
|
||||
defaultHostName, err := os.Hostname()
|
||||
if err != nil {
|
||||
|
||||
@@ -8,11 +8,10 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func (s *Service) SaveHistory() {
|
||||
func (s *Service) SaveHistory(transfers []*Transfer) {
|
||||
if !s.config.GetSaveHistory() {
|
||||
return
|
||||
}
|
||||
transfers := s.GetTransferList()
|
||||
configDir := config.GetConfigDir()
|
||||
historyPath := filepath.Join(configDir, "history.json")
|
||||
historyJson, err := json.Marshal(transfers)
|
||||
|
||||
@@ -34,20 +34,21 @@ const (
|
||||
|
||||
// Transfer
|
||||
type Transfer struct {
|
||||
ID string `json:"id" binding:"required"` // 传输会话 ID
|
||||
CreateTime int64 `json:"create_time"` // 创建时间
|
||||
Sender discovery.Peer `json:"sender" binding:"required"` // 发送者
|
||||
FileName string `json:"file_name"` // 文件名
|
||||
FileSize int64 `json:"file_size"` // 文件大小 (字节)
|
||||
SavePath string `json:"savePath"` // 保存路径
|
||||
Status TransferStatus `json:"status"` // 传输状态
|
||||
Progress Progress `json:"progress"` // 传输进度
|
||||
Type TransferType `json:"type"` // 进度类型
|
||||
ContentType ContentType `json:"content_type"` // 内容类型
|
||||
Text string `json:"text"` // 文本内容
|
||||
ErrorMsg string `json:"error_msg"` // 错误信息
|
||||
Token string `json:"token"` // 用于上传的凭证
|
||||
DecisionChan chan Decision `json:"-"` // 用户决策通道
|
||||
ID string `json:"id" binding:"required"` // 传输会话 ID
|
||||
CreateTime int64 `json:"create_time"` // 创建时间
|
||||
Sender discovery.Peer `json:"sender" binding:"required"` // 发送者
|
||||
// FileName 如果 ContentType 为 file,文件名;如果 ContentType 为 folder,文件夹名;如果 ContentType 为 text,空
|
||||
FileName string `json:"file_name"` // 文件名
|
||||
FileSize int64 `json:"file_size"` // 文件大小 (字节)
|
||||
SavePath string `json:"savePath"` // 保存路径
|
||||
Status TransferStatus `json:"status"` // 传输状态
|
||||
Progress Progress `json:"progress"` // 传输进度
|
||||
Type TransferType `json:"type"` // 进度类型
|
||||
ContentType ContentType `json:"content_type"` // 内容类型
|
||||
Text string `json:"text"` // 文本内容
|
||||
ErrorMsg string `json:"error_msg"` // 错误信息
|
||||
Token string `json:"token"` // 用于上传的凭证
|
||||
DecisionChan chan Decision `json:"-"` // 用户决策通道
|
||||
}
|
||||
|
||||
type TransferOption func(*Transfer)
|
||||
|
||||
@@ -175,6 +175,14 @@ func (s *Service) handleUpload(c *gin.Context) {
|
||||
switch task.ContentType {
|
||||
case ContentTypeFile:
|
||||
destPath := filepath.Join(savePath, task.FileName)
|
||||
// 如果文件已存在则在文件名后追加序号
|
||||
_, err := os.Stat(destPath)
|
||||
counter := 1
|
||||
for err == nil {
|
||||
destPath = filepath.Join(savePath, fmt.Sprintf("%s (%d)%s", strings.TrimSuffix(task.FileName, filepath.Ext(task.FileName)), counter, filepath.Ext(task.FileName)))
|
||||
counter++
|
||||
_, err = os.Stat(destPath)
|
||||
}
|
||||
file, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
// 接收方无法创建文件,直接报错,任务结束
|
||||
@@ -189,17 +197,17 @@ func (s *Service) handleUpload(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
s.receive(c, task, file, ctxReader)
|
||||
s.receive(c, task, Writer{w: file, filePath: destPath}, ctxReader)
|
||||
case ContentTypeText:
|
||||
var buf bytes.Buffer
|
||||
s.receive(c, task, &buf, ctxReader)
|
||||
s.receive(c, task, Writer{w: &buf, filePath: ""}, ctxReader)
|
||||
task.Text = buf.String()
|
||||
case ContentTypeFolder:
|
||||
s.receiveFolder(c, savePath, task, ctxReader)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) receive(c *gin.Context, task *Transfer, writer io.Writer, ctxReader io.Reader) {
|
||||
func (s *Service) receive(c *gin.Context, task *Transfer, writer Writer, ctxReader io.Reader) {
|
||||
// 包装 reader,用于计算进度
|
||||
reader := &PassThroughReader{
|
||||
Reader: ctxReader,
|
||||
@@ -248,6 +256,11 @@ func (s *Service) receive(c *gin.Context, task *Transfer, writer io.Writer, ctxR
|
||||
slog.Error("Failed to write file", "error", err, "component", "transfer")
|
||||
task.Status = TransferStatusError
|
||||
task.ErrorMsg = fmt.Errorf("failed to write file: %v", err).Error()
|
||||
|
||||
// 删除文件
|
||||
if task.ContentType == ContentTypeFile && writer.GetFilePath() != "" {
|
||||
_ = os.Remove(writer.GetFilePath())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -265,6 +278,14 @@ func (s *Service) receiveFolder(c *gin.Context, savePath string, task *Transfer,
|
||||
|
||||
// 创建根目录
|
||||
destPath := filepath.Join(savePath, task.FileName)
|
||||
// 如果文件已存在则在文件名后追加序号
|
||||
_, err := os.Stat(destPath)
|
||||
counter := 1
|
||||
for err == nil {
|
||||
destPath = filepath.Join(savePath, fmt.Sprintf("%s (%d)", task.FileName, counter))
|
||||
counter++
|
||||
_, err = os.Stat(destPath)
|
||||
}
|
||||
if err := os.MkdirAll(destPath, 0755); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, TransferUploadResponse{
|
||||
ID: task.ID,
|
||||
|
||||
@@ -128,13 +128,13 @@ func (s *Service) StoreTransfersToList(transfers []*Transfer) {
|
||||
for _, transfer := range transfers {
|
||||
s.transferList.Store(transfer.ID, transfer)
|
||||
}
|
||||
s.SaveHistory()
|
||||
s.SaveHistory(transfers)
|
||||
s.NotifyTransferListUpdate()
|
||||
}
|
||||
|
||||
func (s *Service) StoreTransferToList(transfer *Transfer) {
|
||||
s.transferList.Store(transfer.ID, transfer)
|
||||
s.SaveHistory()
|
||||
s.SaveHistory([]*Transfer{transfer})
|
||||
s.NotifyTransferListUpdate()
|
||||
}
|
||||
|
||||
@@ -154,12 +154,12 @@ func (s *Service) CleanFinishedTransferList() {
|
||||
}
|
||||
return true
|
||||
})
|
||||
s.SaveHistory()
|
||||
s.SaveHistory(s.GetTransferList())
|
||||
s.NotifyTransferListUpdate()
|
||||
}
|
||||
|
||||
func (s *Service) DeleteTransfer(transferID string) {
|
||||
s.transferList.Delete(transferID)
|
||||
s.SaveHistory()
|
||||
s.SaveHistory(s.GetTransferList())
|
||||
s.NotifyTransferListUpdate()
|
||||
}
|
||||
|
||||
16
internal/transfer/writer.go
Normal file
16
internal/transfer/writer.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package transfer
|
||||
|
||||
import "io"
|
||||
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
filePath string
|
||||
}
|
||||
|
||||
func (w Writer) Write(p []byte) (n int, err error) {
|
||||
return w.w.Write(p)
|
||||
}
|
||||
|
||||
func (w Writer) GetFilePath() string {
|
||||
return w.filePath
|
||||
}
|
||||
Reference in New Issue
Block a user