完成了分享下载全部流程

This commit is contained in:
2024-10-12 13:37:49 +08:00
parent e6f5aaa02f
commit 7862b1d88e
11 changed files with 342 additions and 30 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ var T = Config{
Avatar: "./data/avatar", Avatar: "./data/avatar",
ZeroconfKey: "work_cations_service", ZeroconfKey: "work_cations_service",
ZeroconfPort: 16800, ZeroconfPort: 16800,
ServerAddr: ":16800", ServerAddr: ":16801",
} }
func init() { func init() {
+2
View File
@@ -7,6 +7,7 @@ import (
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"work_cation/assets" "work_cation/assets"
"work_cation/global" "work_cation/global"
"work_cation/service"
"work_cation/version" "work_cation/version"
"work_cation/views" "work_cation/views"
) )
@@ -33,6 +34,7 @@ func main() {
// 主界面 // 主界面
func mainView(w fyne.Window) fyne.CanvasObject { func mainView(w fyne.Window) fyne.CanvasObject {
service.Zeroconf.StartFindService()
var ( var (
content = container.NewMax() content = container.NewMax()
a = fyne.CurrentApp() a = fyne.CurrentApp()
+6 -4
View File
@@ -5,37 +5,39 @@ import (
"fmt" "fmt"
"github.com/grandcat/zeroconf" "github.com/grandcat/zeroconf"
"net" "net"
"work_cation/cfg"
) )
// Online 在线数据 // Online 在线数据
type Online struct { type Online struct {
AddrIPv4 []net.IP `json:"-"` // Host machine IPv4 address AddrIPv4 []net.IP `json:"-"` // Host machine IPv4 address
AddrIPv6 []net.IP `json:"-"` AddrIPv6 []net.IP `json:"-"`
Port int `json:"port"` Port string `json:"port"`
ID string `json:"id"` ID string `json:"id"`
Ip string `json:"ip"` Ip string `json:"ip"`
} }
func (o *Online) Url(router string) string { func (o *Online) Url(router string) string {
return fmt.Sprintf("http://%s:%d%s", o.Ip, o.Port, router) return fmt.Sprintf("http://%s%s%s", o.Ip, o.Port, router)
} }
func UserTOnlineList(user *Users) []string { func UserTOnlineList(user *Users) []string {
return []string{ return []string{
user.ID, user.ID,
user.Ip, user.Ip,
cfg.T.ServerAddr,
} }
} }
func NewOnline(entry *zeroconf.ServiceEntry) (*Online, error) { func NewOnline(entry *zeroconf.ServiceEntry) (*Online, error) {
if len(entry.Text) != 2 { if len(entry.Text) != 3 {
return nil, errors.New("invalid online entry") return nil, errors.New("invalid online entry")
} }
return &Online{ return &Online{
AddrIPv4: entry.AddrIPv4, AddrIPv4: entry.AddrIPv4,
AddrIPv6: entry.AddrIPv6, AddrIPv6: entry.AddrIPv6,
Port: entry.Port, Port: entry.Text[2],
ID: entry.Text[0], ID: entry.Text[0],
Ip: entry.Text[1], Ip: entry.Text[1],
}, nil }, nil
+14 -1
View File
@@ -15,6 +15,19 @@ func (*baseCardRepo) FindAll(db *gorm.DB) []models.BaseCard {
return cards return cards
} }
func (*baseCardRepo) Create(db *gorm.DB, baseCard *models.BaseCard) error { func (*baseCardRepo) Find(db *gorm.DB, uuid string) models.BaseCard {
var card models.BaseCard
db.Where("uuid = ?", uuid).Find(&card)
return card
}
func (*baseCardRepo) CreateOrSave(db *gorm.DB, baseCard *models.BaseCard) error {
var card models.BaseCard
if err := db.Where("uuid = ?", baseCard.UUID).Find(&card).Error; err != nil {
return err
}
if card.UUID != "" {
return db.Where("uuid = ?", baseCard.UUID).Save(baseCard).Error
}
return db.Create(baseCard).Error return db.Create(baseCard).Error
} }
+1 -1
View File
@@ -14,7 +14,7 @@ type BaseCardService struct{}
var BaseCard = &BaseCardService{} var BaseCard = &BaseCardService{}
func (*BaseCardService) Create(db *gorm.DB, baseCard models.BaseCard) { func (*BaseCardService) Create(db *gorm.DB, baseCard models.BaseCard) {
repo.BaseCard.Create(db, &baseCard) repo.BaseCard.CreateOrSave(db, &baseCard)
} }
func (*BaseCardService) Update(db *gorm.DB, updateCard models.BaseCard) { func (*BaseCardService) Update(db *gorm.DB, updateCard models.BaseCard) {
+145 -1
View File
@@ -1,9 +1,17 @@
package service package service
import ( import (
"archive/zip"
"context"
"encoding/json" "encoding/json"
"fmt"
"fyne.io/fyne/v2/data/binding"
"io" "io"
"net/http" "net/http"
"os"
"path/filepath"
"time"
"work_cation/cfg"
"work_cation/models" "work_cation/models"
) )
@@ -14,7 +22,13 @@ var Client = &ClientService{}
func (c *ClientService) GetUser(online *models.Online) (*models.Users, error) { func (c *ClientService) GetUser(online *models.Online) (*models.Users, error) {
var user models.Users var user models.Users
resp, err := http.Get(online.Url("/user")) ctx, carnal := context.WithTimeout(context.TODO(), time.Millisecond*500)
defer carnal()
res, err := http.NewRequestWithContext(ctx, "", online.Url("/user"), nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -29,3 +43,133 @@ func (c *ClientService) GetUser(online *models.Online) (*models.Users, error) {
} }
return &user, nil return &user, nil
} }
func (c *ClientService) GetCards(online *models.Online) ([]models.BaseCard, error) {
var cards []models.BaseCard
ctx, carnal := context.WithTimeout(context.TODO(), time.Millisecond*500)
defer carnal()
res, err := http.NewRequestWithContext(ctx, "", online.Url("/cards"), nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(res)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(data, &cards)
if err != nil {
return nil, err
}
return cards, nil
}
func (c *ClientService) Download(online *models.Online, uuid string, progress binding.Float) error {
resp, err := http.Get(online.Url("/download/card/" + uuid))
if err != nil {
return err
}
defer resp.Body.Close()
downloadFile := filepath.Join(cfg.T.CardDir, fmt.Sprintf("%s.zip", uuid))
_, err = os.Stat(downloadFile)
if err == nil {
os.Remove(downloadFile)
}
file, err := os.Create(downloadFile)
if err != nil {
return err
}
defer file.Close()
r := Rr{
file: file,
tot: resp.ContentLength,
progress: 0,
oneUp: 0,
progressBind: progress,
}
_, err = io.Copy(&r, resp.Body)
if err != nil {
return err
}
// 解压
destDir := filepath.Join(cfg.T.CardDir, uuid)
_ = os.RemoveAll(destDir)
if err = unzipFile(downloadFile, destDir); err != nil {
return err
}
_ = os.Remove(downloadFile)
return nil
}
type Rr struct {
file *os.File
tot int64
progress int64
oneUp int
progressBind binding.Float
}
func (r *Rr) Write(p []byte) (n int, err error) {
n, err = r.file.Write(p)
if err != nil {
return
}
r.progress += int64(n)
nowUp := int(r.progress * 100 / r.tot)
if nowUp != r.oneUp {
r.progressBind.Set(float64(nowUp))
r.oneUp = nowUp
}
return
}
func unzipFile(zipFile, destDir string) error {
// 打开ZIP文件
r, err := zip.OpenReader(zipFile)
if err != nil {
return err
}
defer r.Close()
// 创建目标目录
err = os.MkdirAll(destDir, os.ModePerm)
if err != nil {
return err
}
// 遍历ZIP文件中的文件并解压缩到目标目录
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return err
}
// 创建文件
path := filepath.Join(destDir, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(path, os.ModePerm)
} else {
// 创建文件
file, err := os.Create(path)
if err != nil {
return err
}
_, err = io.Copy(file, rc)
if err != nil {
return err
}
file.Close()
}
rc.Close()
}
return nil
}
+1 -1
View File
@@ -39,7 +39,7 @@ func (*erlangCardService) Create(erlangCard *models.ErlangCards) error {
return global.DB.Transaction(func(tx *gorm.DB) error { return global.DB.Transaction(func(tx *gorm.DB) error {
var baseCard = erlangCard.BaseCard var baseCard = erlangCard.BaseCard
err = repo.BaseCard.Create(global.DB, &baseCard) err = repo.BaseCard.CreateOrSave(global.DB, &baseCard)
return err return err
}) })
} }
+1 -1
View File
@@ -49,6 +49,6 @@ func (*ExecFileService) Create(info *models.ExecFiles, chooseDir string) error {
} }
return global.DB.Transaction(func(tx *gorm.DB) error { return global.DB.Transaction(func(tx *gorm.DB) error {
var baseCard = info.BaseCard var baseCard = info.BaseCard
return repo.BaseCard.Create(tx, &baseCard) return repo.BaseCard.CreateOrSave(tx, &baseCard)
}) })
} }
+80 -1
View File
@@ -1,10 +1,15 @@
package service package service
import ( import (
"archive/zip"
"errors" "errors"
"fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"io"
"log" "log"
"net/http" "net/http"
"os"
"path/filepath"
"work_cation/cfg" "work_cation/cfg"
"work_cation/global" "work_cation/global"
"work_cation/repo" "work_cation/repo"
@@ -52,11 +57,35 @@ func (s *serverService) StatusOffline() error {
} }
func (s *serverService) StartListenServer() error { func (s *serverService) StartListenServer() error {
router := gin.New() router := gin.Default()
router.GET("/user", func(c *gin.Context) { router.GET("/user", func(c *gin.Context) {
user := repo.User.GetUserInfo(global.DB) user := repo.User.GetUserInfo(global.DB)
c.JSON(200, user) c.JSON(200, user)
}) })
router.GET("/cards", func(c *gin.Context) {
cards := repo.BaseCard.FindAll(global.DB)
c.JSON(200, cards)
})
router.GET("/download/card/:uuid", func(c *gin.Context) {
uuid := c.Param("uuid")
card := repo.BaseCard.Find(global.DB, uuid)
fmt.Println("card:", card)
if card.UUID == "" {
c.JSON(404, nil)
return
}
cardPath := filepath.Join(cfg.T.CardDir, card.UUID)
// 设置响应头
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip", uuid))
c.Header("Content-Type", "application/zip")
err := zipFolder(cardPath, c.Writer)
if err != nil {
c.JSON(500, err)
return
}
})
srv := &http.Server{ srv := &http.Server{
Addr: cfg.T.ServerAddr, Addr: cfg.T.ServerAddr,
@@ -71,3 +100,53 @@ func (s *serverService) StartListenServer() error {
s.server = srv s.server = srv
return nil return nil
} }
func zipFolder(folderPath string, zipFile io.Writer) error {
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
err := filepath.Walk(folderPath, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name, err = filepath.Rel(folderPath, filePath)
if err != nil {
return err
}
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
return nil
}
+15 -14
View File
@@ -4,13 +4,14 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/grandcat/zeroconf" "github.com/grandcat/zeroconf"
"time" "slices"
"work_cation/cfg" "work_cation/cfg"
"work_cation/models" "work_cation/models"
) )
type zeroconfService struct { type zeroconfService struct {
server *zeroconf.Server server *zeroconf.Server
onlines []models.Online
} }
var Zeroconf = &zeroconfService{} var Zeroconf = &zeroconfService{}
@@ -37,31 +38,31 @@ func (s *zeroconfService) Close() {
} }
// FindService 查找被发现服务 // FindService 查找被发现服务
func (s *zeroconfService) FindService() (chan *models.Online, error) { func (s *zeroconfService) FindService() ([]models.Online, error) {
return s.onlines, nil
}
func (s *zeroconfService) StartFindService() error {
resolver, err := zeroconf.NewResolver(nil) resolver, err := zeroconf.NewResolver(nil)
if err != nil { if err != nil {
return nil, err return err
} }
allEntries := make(chan *zeroconf.ServiceEntry) allEntries := make(chan *zeroconf.ServiceEntry)
useEntries := make(chan *models.Online)
go func(results <-chan *zeroconf.ServiceEntry) { go func(results <-chan *zeroconf.ServiceEntry) {
for entry := range results { for entry := range results {
fmt.Println(entry.AddrIPv4, entry.ServiceInstanceName())
if entry.ServiceInstanceName() == fmt.Sprintf("%s._http._tcp.local.", cfg.T.ZeroconfKey) { if entry.ServiceInstanceName() == fmt.Sprintf("%s._http._tcp.local.", cfg.T.ZeroconfKey) {
online, err := models.NewOnline(entry) online, err := models.NewOnline(entry)
if err != nil { if err != nil {
continue continue
} }
useEntries <- online delOnlines := slices.DeleteFunc(s.onlines, func(item models.Online) bool {
return item.ID == online.ID
})
s.onlines = append(delOnlines, *online)
} }
} }
}(allEntries) }(allEntries)
ctx, cancel := context.WithCancel(context.Background()) err = resolver.Browse(context.Background(), "_http._tcp", "local.", allEntries)
err = resolver.Browse(ctx, "_http._tcp", "local.", allEntries) return err
if err != nil {
cancel()
return nil, err
}
time.AfterFunc(10*time.Second, cancel)
return useEntries, nil
} }
+75 -4
View File
@@ -1,10 +1,14 @@
package views package views
import ( import (
"fmt"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"time"
"work_cation/global" "work_cation/global"
"work_cation/models" "work_cation/models"
"work_cation/repo" "work_cation/repo"
@@ -18,11 +22,11 @@ func otherUser(w fyne.Window) fyne.CanvasObject {
return widget.NewLabel("网络异常请稍后尝试") return widget.NewLabel("网络异常请稍后尝试")
} }
go func() { go func() {
for online := range findService { for _, online := range findService {
var ( var (
onlineCopy = online onlineCopy = online
) )
baseCardV, err := itemOnlineUserView(w, onlineCopy) baseCardV, err := itemOnlineUserView(w, &onlineCopy)
if err != nil { if err != nil {
continue continue
} }
@@ -69,9 +73,76 @@ func itemOnlineUserView(w fyne.Window, data *models.Online) (fyne.CanvasObject,
}) })
showErrButton := widget.NewButton("查看主页", func() { showErrButton := widget.NewButton("查看主页", func() {
dialog.ShowInformation("错误", "", w) otherUserIndexWin(user, data)
}) })
card := widget.NewCard(user.Name, user.Ip, container.NewVBox(followLabel, showErrButton, followButton)) card := widget.NewCard(user.Name, user.Ip, container.NewVBox(followLabel, showErrButton, followButton))
return card, nil return card, nil
} }
func otherUserIndexWin(user *models.Users, data *models.Online) {
myApp := fyne.CurrentApp()
myWindow := myApp.NewWindow(user.Name)
myWindow.CenterOnScreen()
myWindow.Resize(fyne.NewSize(620, 500))
myWindow.SetContent(otherUserIndexView(myWindow, data))
myWindow.Show()
}
func otherUserIndexView(w fyne.Window, online *models.Online) fyne.CanvasObject {
gridWrap := container.NewGridWrap(fyne.NewSize(200, 200))
cards, _ := service.Client.GetCards(online)
for _, baseCard := range cards {
var baseCardCopy = baseCard
baseCardV := baseOtherCardView(w, baseCardCopy, online)
gridWrap.Add(baseCardV)
}
scroll := container.NewScroll(gridWrap)
return container.NewBorder(nil, nil, nil, nil, scroll)
}
func baseOtherCardView(w fyne.Window, baseCardCopy models.BaseCard, online *models.Online) *widget.Card {
saveCard := repo.BaseCard.Find(global.DB, baseCardCopy.UUID)
infoWid := container.NewVBox(
widget.NewLabel(baseCardCopy.ToolType),
widget.NewLabel(fmt.Sprintf("本地:%s", saveCard.UpdateTx.Format(time.DateTime))),
widget.NewLabel(fmt.Sprintf("作者:%s", baseCardCopy.UpdateTx.Format(time.DateTime))),
)
baseCardV := widget.NewCard(baseCardCopy.Title, baseCardCopy.Text, container.NewBorder(nil, widget.NewToolbar(
widget.NewToolbarAction(theme.DownloadIcon(), func() {
b := saveCard.UpdateTx.Unix() == baseCardCopy.UpdateTx.Unix()
fmt.Println(saveCard.UpdateTx, baseCardCopy.UpdateTx)
if b {
dialog.ShowInformation("结果", "已下载本地", w)
return
}
// 下载到本地 若没关注自动关注
progress := binding.NewFloat()
progress.Set(0)
progressBar := widget.NewProgressBarWithData(progress)
progressBar.Max = 100
smaillWin := dialog.NewCustom("下载中", "关闭", progressBar, w)
smaillWin.Show()
err := service.Client.Download(online, baseCardCopy.UUID, progress)
if err != nil {
smaillWin.SetDismissText("失败")
return
}
// 修改数据
progress.Set(99)
saveCard.UpdateTx = baseCardCopy.UpdateTx
err = repo.BaseCard.CreateOrSave(global.DB, &baseCardCopy)
if err != nil {
smaillWin.SetDismissText("失败")
return
}
progress.Set(100)
smaillWin.SetDismissText("完成 请去[我的]查看")
}),
), nil, nil, infoWid))
return baseCardV
}