From ebeefc215c78ade57809525ab3170260d7780c6f Mon Sep 17 00:00:00 2001 From: xc Date: Fri, 15 Aug 2025 16:37:07 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4go=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aufs/core/baseinfo.go | 58 ++++++ aufs/core/serverinfo.go | 141 +++++++++++++++ aufs/db/dbconst.go | 9 + aufs/db/sqliteDb.go | 100 ++++++++++ aufs/go.mod | 28 ++- aufs/go.sum | 85 +++++++++ aufs/util/fsutil.go | 391 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 811 insertions(+), 1 deletion(-) create mode 100644 aufs/core/baseinfo.go create mode 100644 aufs/core/serverinfo.go create mode 100644 aufs/db/dbconst.go create mode 100644 aufs/db/sqliteDb.go create mode 100644 aufs/go.sum create mode 100644 aufs/util/fsutil.go diff --git a/aufs/core/baseinfo.go b/aufs/core/baseinfo.go new file mode 100644 index 0000000..bd02b68 --- /dev/null +++ b/aufs/core/baseinfo.go @@ -0,0 +1,58 @@ +package core + +import ( + "aufs/db" + "aufs/util" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +type Brespone struct { + Status string `json:"status"` + Data []db.StFileInfo `json:"data"` //目录下的文件 +} + +// 输出数据库中的基础文件的结构和hash +func BfsInfo(w http.ResponseWriter, r *http.Request) { + // 监听的目录通过?p=的方式传入 + urlpath := r.URL.Query().Get("p") + fmt.Printf("your params:%s\n", urlpath) + // 防止逃逸,造成漏洞 + if strings.Contains(urlpath, "../") { + // urlpath = "Lg==" + urlpath = util.Bas64end(".") + } + + // dsrpath := util.Base64dec(urlpath) + // 如果根目录 + if urlpath == "" || urlpath == "." || urlpath == "Lg==" { + urlpath = util.Bas64end(".") + // urlpath = "Lg==" + } + + fmt.Printf("after chk:%s\n", urlpath) + + // 链接数据库 + db.Init() + // 获取请求的信息 + flists := db.Fquery(urlpath) + // fmt.Printf("stfile :%v\n", flist) + // + bfslist := make([]db.StFileInfo, 0) + // + for _, v := range flists { + bfslist = append(bfslist, v) + } + + // fmt.Fprintf(w, "%s:sqlite query ...", r.Host) + response := Brespone{ + Status: "success", + Data: bfslist, + } + + // 开启跨域 + uCorsHadler(w, r) + json.NewEncoder(w).Encode(response) +} diff --git a/aufs/core/serverinfo.go b/aufs/core/serverinfo.go new file mode 100644 index 0000000..4f58402 --- /dev/null +++ b/aufs/core/serverinfo.go @@ -0,0 +1,141 @@ +package core + +import ( + "aufs/config" + "aufs/db" + "aufs/util" + "encoding/json" + "fmt" + "net/http" + "os" + "path/filepath" + "strings" +) + +// json 结构体 +type Response struct { + Status string `json:"status"` //状态 + Data FilesListJson `json:"data"` //目录下的文件 + Curdir string `json:"curdir"` // 扫描的目录 + WorksDir string `json:"workdir"` //监听目录 + Hostip string `json:"hostip"` //运行的主机ip +} + +// 文件输出的结构 +type FileJson struct { + Fname string `json:"fname"` + Dirflag bool `json:"dirflag"` + Isbackup int `json:"isbackup"` + Fhash string `json:"hash"` // hash + Fsize string `json:"size"` //文件大小, 输出带单位的大小 + Frehash string `json:"rehash"` // 参考hash +} + +type FilesListJson struct { + Flist []FileJson `json:"list"` +} + +// 获取运行的路径 +var Gpath string + +// 遍历监视目录,发送到json中 +func SerInfo(w http.ResponseWriter, r *http.Request) { + // 监听的目录通过?p=的方式传入 + urlpath := r.URL.Query().Get("p") + // 防止逃逸,造成漏洞 + if strings.Contains(urlpath, "../") || urlpath == "" { + urlpath = "." + } + + // urlpath 进行base64 解码 + dsrpath := util.Base64dec(urlpath) + // 监听的根目录 + realFilePath := filepath.Join(config.G.FilePath, dsrpath) + // 时间目录的情况 + fileInfo, err := os.Stat(realFilePath) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // 链接数据库 + db.Init() + // 获取请求的信息 + rcflists := db.Fquery(util.Bas64end(dsrpath)) + // + // list json + var flist FilesListJson + //针对目录的情况才输出 + // todo 如果是文件的话 暂时不处理 + if fileInfo.IsDir() { + // 遍历目录 + files, err := os.ReadDir(realFilePath) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // 遍历 + for _, v := range files { + // fmt.Printf("rang v:%v\n", v) + isbak := 0 + rchash := "" //参考的hash + // 如果有 backup + if strings.Contains(v.Name(), "backup") { + isbak = 1 + } + // 如果是文件的话,就计算hash和大小 + if v.IsDir() { + //While entry is A Directory + flist.Flist = append(flist.Flist, FileJson{Fname: v.Name(), Dirflag: v.IsDir(), Isbackup: isbak, Fhash: "", Fsize: "", Frehash: ""}) + } else { + // 计算文件的hash + funame := filepath.Join(realFilePath, v.Name()) + fhash := util.CalacHash(funame) + // + for _, av := range rcflists { + if av.Fname == v.Name() { + rchash = av.Fhash + } + } + // 文件大小 + // 获取文件大小(以字节为单位) + sizeInBytes := fileInfo.Size() + sizeStr := fmt.Sprintf("%.2f KB", float64(sizeInBytes)/1024) + // output + flist.Flist = append(flist.Flist, FileJson{Fname: v.Name(), Dirflag: v.IsDir(), Isbackup: isbak, Fhash: fhash, Fsize: sizeStr, Frehash: rchash}) + } + + } + + } + + // respone file list + response := Response{ + Status: "success", + Curdir: urlpath, + WorksDir: config.G.FilePath, + Hostip: config.G.LocalIP, + Data: flist, + } + + // 开启跨域 + uCorsHadler(w, r) + json.NewEncoder(w).Encode(response) +} + +// 跨域函数 +func uCorsHadler(w http.ResponseWriter, r *http.Request) { + // 设置跨域响应头 + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS,PUT,DELETET") + // w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Accept,Accept-Length,Accept-Encoding,X-XSRF-TOKEN,X-XSRF-TOKEN") + w.Header().Set("Access-Control-Allow-Headers", "*") + // + w.Header().Set("Content-Type", "application/json") + + // 如果是OPTIONS请求,返回200 OK + if r.Method == "OPTIONS" { + // fmt.Printf("options is now \n") + // w.WriteHeader(http.StatusOK) + return + } +} diff --git a/aufs/db/dbconst.go b/aufs/db/dbconst.go new file mode 100644 index 0000000..d83b0f6 --- /dev/null +++ b/aufs/db/dbconst.go @@ -0,0 +1,9 @@ +package db + +// 定义数据库中用的模型 +type StFileInfo struct { + Id string `json:"id"` + Fpath string `json:"fpath"` + Fhash string `json:"fhash"` + Fname string `json:"fname"` +} diff --git a/aufs/db/sqliteDb.go b/aufs/db/sqliteDb.go new file mode 100644 index 0000000..da10ff4 --- /dev/null +++ b/aufs/db/sqliteDb.go @@ -0,0 +1,100 @@ +package db + +import ( + "fmt" + + // 导入包,导入前缀为下划线,则init函数被执行,然后注册驱动。 + "github.com/jmoiron/sqlx" + _ "github.com/logoove/sqlite" +) + +var db *sqlx.DB +var err error + +func Init() { + // fmt.Printf("hey,I am initilize function in SqliteDb\n")s + // + // Open() 函数指定驱动名称和数据源名称 + db, err = sqlx.Open("sqlite", "ups.db") + if err != nil { + fmt.Printf("Database creation failed: %v\n", err) + return + } + // 调用db.Close() 函数,确保关闭数据库并阻止启动新的查询 + // defer db.Close() + // + connectDB() +} + +// 连接数据库 +func connectDB() { + var version string + // QueryRow() 执行查询,返回版本号 + err = db.QueryRow("SELECT SQLITE_VERSION()").Scan(&version) + if err != nil { + fmt.Printf("Database creation failed: %v\n", err) + return + } + // 连接成功,打印出"database connected:版本号" + fmt.Printf("Database creation successful: %v\n", version) +} + +// 创建数据库表 +func CreateTable() { + // 建表语句 + sts := ` + CREATE TABLE f_info ( + id INTEGER PRIMARY KEY NOT NULL, + fname TEXT NOT NULL, + fpath TEXT NOT NULL, + fhash TEXT NOT NULL + );` + + // 使用db.Exec() 函数来执行 SQL 语句 + _, err = db.Exec(sts) + if err != nil { + fmt.Printf("Failed to create database table: %v\n", err) + return + } + fmt.Printf("Successfully created database table! \n") +} + +// 插入数据 +func InsertStf(sf StFileInfo) { + // 插入语句 + res, err := db.Exec("INSERT INTO f_info(fname, fpath,fhash) VALUES(?,?,?)", sf.Fname, sf.Fpath, sf.Fhash) + if err != nil { + fmt.Printf("Insert data failed: %v\n", err) + return + } + + // 获取自增ID + lastInsertId, _ := res.LastInsertId() + fmt.Printf("Successfully inserted data, lastInsertId = %v\n", lastInsertId) +} + +// 查询数据 +func Fquery(fpbs string) (stlist []StFileInfo) { + // 结果重量集合 + stlist = make([]StFileInfo, 0) + + // 查询语句 + rows, err := db.Query("SELECT id,fname,fpath,fhash FROM f_info WHERE fpbs = ?", fpbs) + // + if err != nil { + fmt.Printf("Failed to query data: %v\n", err) + return + } + // FOR loop + for rows.Next() { + // var weight float64 + var st StFileInfo + err = rows.Scan(&st.Id, &st.Fname, &st.Fpath, &st.Fhash) + if err != nil { + fmt.Printf("Failed to read data: %v\n", err) + continue + } + stlist = append(stlist, st) + } + return stlist +} diff --git a/aufs/go.mod b/aufs/go.mod index 1af3a65..1b7ac3a 100644 --- a/aufs/go.mod +++ b/aufs/go.mod @@ -1,3 +1,29 @@ module aufs -go 1.22.1 +go 1.23.0 + +toolchain go1.24.6 + +require ( + github.com/jmoiron/sqlx v1.4.0 + github.com/logoove/sqlite v1.37.1 + github.com/schollz/progressbar/v3 v3.18.0 + google.golang.org/protobuf v1.36.7 +) + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rivo/uniseg v0.4.7 // indirect + golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/term v0.28.0 // indirect + modernc.org/libc v1.66.3 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect + modernc.org/sqlite v1.38.2 // indirect +) diff --git a/aufs/go.sum b/aufs/go.sum new file mode 100644 index 0000000..c9a4485 --- /dev/null +++ b/aufs/go.sum @@ -0,0 +1,85 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= +github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/logoove/sqlite v1.37.1 h1:smJKG1VTnFdkxLWLeeh7Sy+xvK6yYg+u7dXutj9c7LM= +github.com/logoove/sqlite v1.37.1/go.mod h1:fLu3SyDziw8V0K6THYj0jTk6Z+VBbRIoK843CglTWME= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA= +github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM= +modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= +modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= +modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM= +modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= +modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= +modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/aufs/util/fsutil.go b/aufs/util/fsutil.go new file mode 100644 index 0000000..c5c7987 --- /dev/null +++ b/aufs/util/fsutil.go @@ -0,0 +1,391 @@ +package util + +import ( + "archive/zip" + "aufs/config" + "bytes" + "crypto/sha256" + "encoding/base64" + "fmt" + "io" + "mime/multipart" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + "github.com/schollz/progressbar/v3" +) + +// 遍历目录下的文件 +// 是否只扫相对路径下 +func GetDirFilePaths(dirPath string, relativeOnly bool) ([]string, error) { + var filePaths []string + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + if relativeOnly { + fileName := filepath.Base(path) + relativePath := filepath.ToSlash(filepath.Join(filepath.Base(dirPath), fileName)) + filePaths = append(filePaths, relativePath) + } else { + filePaths = append(filePaths, filepath.ToSlash(path)) + } + } + return nil + }) + if err != nil { + return nil, err + } + return filePaths, nil +} + +// base64 encode +// url safe +func Bas64end(str string) string { + // bdata:= + return base64.URLEncoding.EncodeToString([]byte(str)) +} + +// base64 url safe uneconde +func Base64dec(bsstr string) string { + dedc, _ := base64.URLEncoding.DecodeString(bsstr) + return string(dedc) +} + +// 计算文件的hash +func CalacHash(rfile string) string { + // 获取到真实地址 + // + file, err := os.Open(rfile) + if err != nil { + panic(err) + } + defer file.Close() + + // initlize hash object + hash := sha256.New() + + // write file into hash object + if _, err := io.Copy(hash, file); err != nil { + panic(err) + } + // get hash value + hashBytes := hash.Sum(nil) + //converto to hash string + hastString := fmt.Sprintf("%x", hashBytes) + return hastString +} + +// 判断文件是否存在 +func IsFileExist(filename string) bool { + if _, err := os.Stat(filename); err == nil { + return true + } + return false +} + +// 发送文件 +func SendFiles(filePath string, url string) error { + filePath = filepath.ToSlash(filePath) + fileInfo, err := os.Stat(filePath) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("file [%s] not exist", filePath) + } + return fmt.Errorf("file [%s] error: %w", filePath, err) + } + + if fileInfo.IsDir() { + return postDirectory(filePath, url) + } + + return postFile(filePath, path.Base(filePath), url) +} + +// 传送文件夹 +func postDirectory(dirPath string, url string) error { + files, err := GetDirFilePaths(dirPath, false) + if err != nil { + return err + } + + fmt.Println("\nAll files in folder:") + for _, file := range files { + fmt.Println(file) + } + + var confirm string + fmt.Print("\nTransfer all files? [Y/N] ") + fmt.Scanln(&confirm) + if strings.ToLower(confirm) != "y" { + fmt.Print("\nCancel send all files ") + return nil + } + + for _, file := range files { + fileName, _ := filepath.Rel(dirPath, file) + fileName = filepath.Join(filepath.Base(dirPath), fileName) + err := postFile(file, fileName, url) + if err != nil { + return err + } + } + fmt.Printf("Send folder %s success.\n", dirPath) + return nil +} + +// 传送文件 +func postFile(wfpath string, filename string, url string) error { + payload := &bytes.Buffer{} + writer := multipart.NewWriter(payload) + + file, err := os.Open(wfpath) + if err != nil { + return err + } + defer file.Close() + + part, err := writer.CreateFormFile("file", filepath.ToSlash(filename)) + if err != nil { + return err + } + + fileInfo, _ := file.Stat() + bar := progressbar.DefaultBytes( + fileInfo.Size(), + fmt.Sprintf("Uploading [%s]", filename), + ) + + _, err = io.Copy(io.MultiWriter(part, bar), file) + if err != nil { + return err + } + + err = writer.Close() + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPost, url, payload) + if err != nil { + return err + } + req.Header.Set("Content-Type", writer.FormDataContentType()) + // 在头里面加上路径,去除前缀 + rlpath := strings.TrimPrefix(wfpath, config.G.FilePath) + // fmt.Printf("wfpath:%s,rel path is:%s", wfpath, rlpath) + req.Header.Set("Fpath", Bas64end(rlpath)) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("upload failed with status code: %d", resp.StatusCode) + } + + return nil +} + +/* +* add file to zip + */ +func ZipFiles(zipname string, files []string, zippath string) error { + fmt.Printf("zipname:%s\n", zipname) + // 创建zip文件 + newZipFile, err := os.Create(zipname) + if err != nil { + return err + } + + defer newZipFile.Close() + // zip写头 + zipWriter := zip.NewWriter(newZipFile) + defer zipWriter.Close() + + // 把files添加到zip中 + for _, file := range files { + // + fmt.Printf("zipfiles in function :%s\n", file) + + // + zipfile, err := os.Open(file) + if err != nil { + return err + } + defer zipfile.Close() + + // + info, err := zipfile.Stat() + if err != nil { + return err + } + // + header, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + // 在zip存档中设置文件的相对路径 + header.Name, err = filepath.Rel(filepath.Dir(config.G.FilePath), file) + if err != nil { + return err + } + // 转换为Unix风格的路径分隔符 + header.Name = filepath.ToSlash(header.Name) + // 目录需要拼上一个 "/" ,否则会出现一个和目录一样的文件在压缩包中 + if info.IsDir() { + header.Name += "/" + } else { + // 将压缩方法设置为deflate + header.Method = zip.Deflate + } + // + fmt.Printf("header.name is %s\n", header.Name) + + writer, err := zipWriter.CreateHeader(header) + if err != nil { + return err + } + + if _, err = io.Copy(writer, zipfile); err != nil { + return err + } + } + return nil +} + +// 解压缩zip文件 +// 这个方法必须是首字母大写的,才能被注册 +// src: 需要解压的zip文件 +func DecompressZip(zpFname string) error { + // 下载文件 + archive, err := zip.OpenReader(zpFname) + if err != nil { + return err + } + // 取zip文件的绝对路径的文件夹。会默认解压到files下,暂停使用 + // dir := filepath.Dir(zpFname) + defer archive.Close() + // 遍历目录 + for _, f := range archive.File { + // zip 解压的时候,路径为监听的路径 + filePath := filepath.Join(config.G.FilePath, f.Name) + // 暂停原因 屏蔽解压到files 目录下 + //filePath := filepath.Join(dir, f.Name) + if f.FileInfo().IsDir() { + os.MkdirAll(filePath, os.ModePerm) + continue + } + // 父文件夹开始创建目录 + if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + return fmt.Errorf("failed to make directory (%v)", err) + } + // 文件原有模式 + dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return fmt.Errorf("failed to create file (%v)", err) + } + fileInArchive, err := f.Open() + if err != nil { + return fmt.Errorf("failed to open file in zip (%v)", err) + } + if _, err := io.Copy(dstFile, fileInArchive); err != nil { + return fmt.Errorf("failed to copy file in zip (%v)", err) + } + dstFile.Close() + fileInArchive.Close() + } + return nil +} + +// dest:目的地址以及压缩文件名 eg:/data/logZip/2022-12-12-log.zip +// paths:需要压缩的所有文件所组成的集合 +func CompressToZip(dest string, currentPath string, paths []string) error { + // fmt.Println("real path", currentPath) + // 打开files 目录 + filesPath := "" + if dir, err := os.Getwd(); err == nil { + filesPath = dir + "/sync_zips/" + } + if err := os.MkdirAll(filesPath, os.ModePerm); err != nil { + fmt.Println(err.Error()) + } + // ToSlash 过滤windows的斜杠引起的bug + zfile, err := os.Create(path.Join("./sync_zips", "/", dest)) + if err != nil { + return err + } + defer zfile.Close() + zipWriter := zip.NewWriter(zfile) + defer zipWriter.Close() + + // 遍历带压缩的目录信息 + for _, src := range paths { + // 替换文件名,并且去除前后 "\" 或 "/" + // path = strings.Trim(path, string(filepath.Separator)) + // 从配置中读取到源目录 + src = path.Join(currentPath, "/", src) + // 删除尾随路径(如果它是目录) + src := strings.TrimSuffix(src, string(os.PathSeparator)) + // 文件遍历 + err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + // 创建本地文件头 + header, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + // 监听的工作目录作为文件的结尾,然后取相对路径 + // 在zip存档中设置文件的相对路径 + header.Name, err = filepath.Rel(filepath.Dir(config.G.FilePath), path) + if err != nil { + return err + } + + // 转换为Unix风格的路径分隔符 + header.Name = filepath.ToSlash(header.Name) + + // 目录需要拼上一个 "/" ,否则会出现一个和目录一样的文件在压缩包中 + if info.IsDir() { + header.Name += "/" + } else { + // 将压缩方法设置为deflate + header.Method = zip.Deflate + } + // fmt.Printf("zip header:%v\n", header.Name) + // 创建写入头的writer + headerWriter, err := zipWriter.CreateHeader(header) + if err != nil { + return err + } + if info.IsDir() { + return nil + } + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(headerWriter, f) + return err + }) + + if err != nil { + return err + } + } + // 存到指定位置 + os.Rename(dest, path.Join("./sync_zips/", dest)) + return nil +}