sync.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "os/signal"
  7. "path/filepath"
  8. "regexp"
  9. "strings"
  10. "syscall"
  11. "time"
  12. "git.wecise.com/wecise/util/ssh"
  13. "github.com/spf13/cast"
  14. ccfg "github.com/wecisecode/util/cfg"
  15. "github.com/wecisecode/util/filewalker"
  16. clog "github.com/wecisecode/util/logger"
  17. )
  18. func init() {
  19. exitChan := make(chan os.Signal, 1)
  20. signal.Notify(exitChan, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGABRT, syscall.SIGTERM)
  21. go func() {
  22. <-exitChan
  23. os.Exit(1)
  24. }()
  25. }
  26. var config = ccfg.MConfig()
  27. var logger = clog.New()
  28. // 同步指定文件夹中的内容
  29. func PrintUsage() {
  30. println(`Usage:
  31. sync <frompath> <topath> [<copytopath> ...]
  32. `)
  33. }
  34. var option = struct {
  35. Debug bool
  36. }{
  37. Debug: false,
  38. }
  39. func main() {
  40. var frompath string = ""
  41. var topath string = ""
  42. var copytopath = []string{}
  43. for _, arg := range os.Args[1:] {
  44. if strings.HasPrefix(arg, "-") {
  45. switch arg {
  46. case "-debug":
  47. option.Debug = true
  48. }
  49. } else if arg != "" {
  50. if frompath == "" {
  51. frompath = arg
  52. } else if topath == "" {
  53. topath = arg
  54. } else {
  55. copytopath = append(copytopath, arg)
  56. }
  57. }
  58. }
  59. switch {
  60. case frompath != "" && topath != "":
  61. err := sync(frompath, topath, copytopath...)
  62. if err != nil {
  63. println(err.Error())
  64. os.Exit(1)
  65. }
  66. default:
  67. PrintUsage()
  68. os.Exit(1)
  69. }
  70. }
  71. var REremotepath = regexp.MustCompile(`^([^:]+):([^@]+)@([^/]+)(/.*)`)
  72. var REabspath = regexp.MustCompile(`^((?:/.*)|(?:[a-zA-Z]:\.*)|(?:[a-zA-Z]:))`)
  73. func sync(frompath, topath string, copytopath ...string) (err error) {
  74. frompath, e := filepath.Abs(frompath)
  75. if e != nil {
  76. err = e
  77. return
  78. }
  79. topath, e = filepath.Abs(topath)
  80. if e != nil {
  81. err = e
  82. return
  83. }
  84. fmt.Println("sync from", frompath, "to", topath, "\ncopy to", copytopath)
  85. for {
  86. err = sync_once(frompath, topath, copytopath...)
  87. if err != nil {
  88. return
  89. }
  90. time.Sleep(5 * time.Second)
  91. }
  92. }
  93. func sync_once(frompath, topath string, copytopath ...string) (err error) {
  94. fw, e := filewalker.NewFileWalker([]string{frompath}, `^[^\.].*`) // orderby: dirfirst, filefirst, fullpath
  95. if e != nil {
  96. return e
  97. }
  98. count := 0
  99. e = fw.List(func(basedir, fpath string) bool {
  100. if option.Debug {
  101. fmt.Println("basedir=", basedir, ", fpath=", fpath)
  102. }
  103. absbasedir, e := filepath.Abs(basedir)
  104. if e != nil {
  105. err = e
  106. return false
  107. }
  108. hiddenpath := fmt.Sprintf(`.*\%c\..*`, os.PathSeparator)
  109. if regexp.MustCompile(`^[^\.].*`).MatchString(fpath) && !regexp.MustCompile(hiddenpath).MatchString(fpath) {
  110. fromfile := filepath.Join(absbasedir, fpath)
  111. tofile := filepath.Join(topath, fpath)
  112. if option.Debug {
  113. fmt.Println(time.Now().Format("2006-01-02 15:04:05"), fromfile, " ==> ", tofile)
  114. }
  115. fromfi, e := os.Stat(fromfile)
  116. if e != nil {
  117. err = fmt.Errorf("from file %v", e)
  118. return false
  119. }
  120. tofi, e := os.Stat(tofile)
  121. if e != nil && !os.IsNotExist(e) {
  122. err = fmt.Errorf("to file %v", e)
  123. return false
  124. }
  125. if tofi != nil && fromfi.Size() == tofi.Size() && fromfi.ModTime() == tofi.ModTime() {
  126. return true
  127. }
  128. fmt.Print(fromfile, " ==> ", tofile)
  129. frombs, e := ReadFile(fromfile)
  130. if e != nil {
  131. err = fmt.Errorf("from file %v", e)
  132. return false
  133. }
  134. WriteFile(topath, fpath, frombs, fromfi.ModTime())
  135. for _, ctpath := range copytopath {
  136. WriteFile(ctpath, fpath, frombs, fromfi.ModTime())
  137. }
  138. count++
  139. }
  140. return true
  141. })
  142. if e != nil {
  143. return e
  144. }
  145. if err != nil {
  146. return
  147. }
  148. if count > 0 || option.Debug {
  149. fmt.Println(time.Now().Format("2006-01-02 15:04:05"), count, "files copied")
  150. }
  151. return
  152. }
  153. func ReadFile(name string) (data []byte, err error) {
  154. fmt.Print(" -.--%")
  155. defer fmt.Println()
  156. data = make([]byte, 0, 512)
  157. for {
  158. data, err = ReadFileContinue(name, data)
  159. if err == io.EOF {
  160. err = nil
  161. return
  162. }
  163. }
  164. }
  165. func ReadFileContinue(name string, data []byte) ([]byte, error) {
  166. f, err := os.Open(name)
  167. if err != nil {
  168. return data, err
  169. }
  170. defer f.Close()
  171. var filesize int64
  172. var bufsize int
  173. if info, err := f.Stat(); err == nil {
  174. filesize = info.Size()
  175. if int64(int(filesize)) == filesize {
  176. bufsize = int(filesize)
  177. }
  178. }
  179. bufsize++ // one byte for final read at EOF
  180. // If a file claims a small size, read at least 512 bytes.
  181. // In particular, files in Linux's /proc claim size 0 but
  182. // then do not work right if read in small pieces,
  183. // so an initial read of 1 byte would not work correctly.
  184. offset := int64(len(data))
  185. if bufsize > cap(data) {
  186. ndata := make([]byte, len(data), bufsize)
  187. copy(ndata, data)
  188. data = ndata
  189. }
  190. statuschan := make(chan int64, 100)
  191. go func() {
  192. for range statuschan {
  193. s := fmt.Sprintf("%.2f%%", 100*float64(offset)/float64(filesize))
  194. bs := strings.Repeat("\b", len(s))
  195. fmt.Printf("%s%s", bs, s)
  196. }
  197. if offset >= filesize {
  198. s := fmt.Sprintf("%.2f%%", 100.)
  199. bs := strings.Repeat("\b", len(s))
  200. fmt.Printf("%s%s", bs, s)
  201. }
  202. }()
  203. chunk := int64(1024 * 256)
  204. t := time.Now()
  205. for {
  206. to := offset + chunk
  207. if to > int64(cap(data)) {
  208. to = int64(cap(data))
  209. }
  210. n, err := f.ReadAt(data[offset:to], int64(offset))
  211. offset += int64(n)
  212. statuschan <- offset
  213. if err != nil {
  214. close(statuschan)
  215. return data[:offset], err
  216. }
  217. if offset >= int64(cap(data)) {
  218. d := append(data[:cap(data)], 0)
  219. data = d[:offset]
  220. }
  221. ut := time.Since(t)
  222. if ut < 500*time.Millisecond {
  223. chunk *= 2
  224. } else if chunk > 1024 && ut > 5*time.Second {
  225. chunk /= 2
  226. }
  227. t = time.Now()
  228. }
  229. }
  230. func WriteFile(basedir string, fpath string, bs []byte, mtime time.Time) (err error) {
  231. ss := REremotepath.FindStringSubmatch(basedir)
  232. if len(ss) == 5 {
  233. username, password, hostport, remotepath := ss[1], ss[2], ss[3], regexp.MustCompile("//+").ReplaceAllString(ss[4], "/")
  234. return WriteRemoteFile(username, password, hostport, remotepath, fpath, bs, mtime)
  235. }
  236. tofile := filepath.Join(basedir, fpath)
  237. todir := filepath.Dir(tofile)
  238. e := os.MkdirAll(todir, os.ModePerm)
  239. if e != nil {
  240. err = fmt.Errorf("to dir %v", e)
  241. return
  242. }
  243. e = os.WriteFile(tofile, bs, os.ModePerm)
  244. if e != nil {
  245. err = fmt.Errorf("to file %v", e)
  246. return
  247. }
  248. e = os.Chtimes(tofile, mtime, mtime)
  249. if e != nil {
  250. err = fmt.Errorf("to file %v", e)
  251. return
  252. }
  253. return nil
  254. }
  255. func WriteRemoteFile(username, password, hostport, remotepath, fpath string, bs []byte, mtime time.Time) error {
  256. fpath = strings.ReplaceAll(fpath, "\\", "/")
  257. fmt.Println("copy to:", fmt.Sprint(username, ":", password, "@", hostport, "/", remotepath, "/", fpath))
  258. hp := strings.Split(hostport, ":")
  259. host := hp[0]
  260. port := 22
  261. if len(hp) >= 2 && hp[1] != "" {
  262. port = cast.ToInt(hp[1])
  263. }
  264. node := &ssh.Node{
  265. User: username,
  266. Password: password,
  267. Host: host,
  268. Port: port,
  269. }
  270. client, e := node.Connect()
  271. if e != nil {
  272. return e
  273. }
  274. rfpath := remotepath + "/" + fpath
  275. diridx := strings.LastIndex(rfpath, "/")
  276. e = client.MkdirAll(rfpath[:diridx])
  277. if e != nil {
  278. return e
  279. }
  280. e = client.WriteFile(rfpath, bs, os.ModePerm, mtime)
  281. if e != nil {
  282. return e
  283. }
  284. return nil
  285. }