文件同步
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

1497 行
37 KiB

  1. package handle
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "crypto/md5"
  7. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "fts/config"
  11. "fts/etcdclient"
  12. "github.com/gorilla/websocket"
  13. _ "github.com/ipfs/go-ipfs-api"
  14. shell "github.com/ipfs/go-ipfs-api"
  15. "io"
  16. "io/ioutil"
  17. "log"
  18. "net/http"
  19. "os"
  20. "os/exec"
  21. "path"
  22. "path/filepath"
  23. "strconv"
  24. "strings"
  25. "time"
  26. )
  27. //登陆账号
  28. var gobalLoginUserName string
  29. //登陆账号Id
  30. var gobalLoginUserId string
  31. //key:filepath,value:hash
  32. var gobalFileMap = make(map[string] string)
  33. var gobalFileUpdateTimeMap = make(map[string] string)
  34. //手动上传文件,非自动上传
  35. var goabalAddFileMap = make(map[string] int)
  36. //本地项目空间目录
  37. var gobalLocalProjectDir string
  38. var gobalSubscriptionFileChangeSwitch int =0 //订阅文件变更开关
  39. var ipfsPath=os.Getenv("IPFS-PATH")
  40. /**
  41. 文件上传下载进度
  42. */
  43. type processStruct struct {
  44. Size string `json:"size"`
  45. CurrentSize string `json:"currentSize"`
  46. Unit string `json:"unit"`
  47. CurrentUnit string `json:"currentUnit"`
  48. Process float64 `json:"process"`
  49. Hash string `json:"hash"`
  50. CommitHistoryHash string `json:"commitHistoryHash"`
  51. }
  52. /**
  53. 初始化本地客户端配置,包括ipfs网关、引导节点
  54. @param ipfsApi ipfs网关 例如:http://192.168.1.1:5001
  55. @param ipfsBootstrap ipfs引导节点,多个用;分割 例如:/dns/www.lockingos.org/tcp/4001/p2p/12D3KooWER8uoGrzeLuJHTXoTMnR4jjHfpcA6ZcpXBaQNJvG5jMP
  56. */
  57. func InitClientConfig(ipfsApi,ipfsBootstrap string) error{
  58. //空格路径处理
  59. ipfsPath=strings.Replace(os.Getenv("IPFS-PATH"),"\"","",1)+"\\ipfs.exe"
  60. config.ServerIpfsUrl = ipfsApi
  61. log.Println("配置客户端网关:"+config.ServerIpfsUrl)
  62. ipfsBootstraps := strings.Split(ipfsBootstrap,";")
  63. for _, simpleBootstrap := range ipfsBootstraps {
  64. log.Println("配置引导节点:"+simpleBootstrap)
  65. cmd := exec.Command(ipfsPath,"bootstrap", "add", simpleBootstrap)
  66. err :=cmd.Run()
  67. if err!=nil{
  68. return err
  69. }
  70. }
  71. return nil
  72. }
  73. /**
  74. 初始化本地工作目录
  75. @param userName 用户登陆账号
  76. @param userId 用户ID
  77. @param projectName 项目名称
  78. */
  79. func InitLocalWorkSpace(conn *websocket.Conn, userName, userId, projectName string) (error){
  80. //空格路径处理
  81. ipfsPath=strings.Replace(os.Getenv("IPFS-PATH"),"\"","",1)+"\\ipfs.exe"
  82. //初始化当前登陆用户信息
  83. gobalLoginUserName = userName
  84. gobalLoginUserId = userId
  85. //初始化本地工作空间绝对路径
  86. gobalLocalProjectDir = fmt.Sprint(config.LocalWorkSpaceDir+gobalLoginUserName+"\\"+projectName)
  87. // 检查本地目录是否存在
  88. _,err := os.Stat(gobalLocalProjectDir)
  89. if err != nil {
  90. //创建文件目录
  91. os.MkdirAll(gobalLocalProjectDir, os.ModePerm)
  92. }
  93. log.Println("进入项目空间:"+gobalLocalProjectDir)
  94. if err := conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprint(gobalLocalProjectDir))); err != nil {
  95. log.Println(err)
  96. return err
  97. }
  98. return nil
  99. }
  100. //工作空间增加文件监听事件
  101. func watchWalkfunc(filePath string, info os.FileInfo, err error) error {
  102. if info == nil{
  103. return nil
  104. }
  105. if info.IsDir()==true{
  106. //config.GobalWatch.Remove(filePath)
  107. err = config.GobalWatch.Add(filePath)
  108. if err != nil {
  109. log.Println(err)
  110. return err
  111. }
  112. }
  113. return nil
  114. }
  115. /**
  116. 下载指令
  117. @param hash ipfs哈希值
  118. @param projectName 项目名称
  119. @para fileName 文件名称
  120. @param dir 云文件目录
  121. */
  122. func DownCommand(conn *websocket.Conn, hash, projectName, fileName, nodeDir string) error{
  123. //检查文件目录是否存在,不存在则创建
  124. fileDir := gobalLocalProjectDir+"\\"+nodeDir
  125. _,err := os.Stat(fileDir)
  126. if err != nil {
  127. //创建文件目录
  128. err = os.MkdirAll(fileDir, os.ModePerm)
  129. if err!=nil{
  130. log.Println(err)
  131. return err
  132. }
  133. }
  134. //下载启动标识,有下载进度则设置为true
  135. var downloading bool = false
  136. //构建本地cmd执行 ipfs get
  137. progress := make(chan string,10000)
  138. var stdout, stderr []byte
  139. var errStdout, errStderr error
  140. cmd := exec.Command(ipfsPath,"get", hash,"-o",fmt.Sprint(fileDir+"\\"+fileName))
  141. stdoutIn, _ := cmd.StdoutPipe()
  142. stderrIn, _ := cmd.StderrPipe()
  143. cmd.Start()
  144. go func() {
  145. stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn, progress)
  146. }()
  147. go func() {
  148. stderr, errStderr = copyAndCapture(os.Stderr, stderrIn, progress)
  149. }()
  150. log.Println("资源连接中...")
  151. //异步定时读取进度反馈给前端,每500ms返回一次进度
  152. go func(){
  153. millSeconds := time.Now().UnixNano() / 1e6
  154. for content := range progress { // 通道关闭后会退出for range循环
  155. current :=time.Now().UnixNano() / 1e6
  156. if !downloading{
  157. log.Println("资源连接成功,下载中...")
  158. }
  159. downloading = true
  160. if current-millSeconds>500{
  161. projson,err := contentToJSONByte(content)
  162. if projson==nil && err==nil{
  163. continue
  164. }
  165. if err != nil {
  166. log.Printf("json.Marshal error %s\n", err)
  167. }
  168. //设置下载启动标识和下载时间戳
  169. millSeconds = current
  170. //反馈前端
  171. if err := conn.WriteMessage(websocket.TextMessage, projson); err != nil {
  172. log.Println(err)
  173. break
  174. }
  175. }
  176. if strings.Index(content,"100.00%")!=-1{
  177. projson,err := contentToJSONByte(content)
  178. if projson==nil && err==nil{
  179. continue
  180. }
  181. if err != nil {
  182. log.Printf("json.Marshal error %s\n", err)
  183. }
  184. if err := conn.WriteMessage(websocket.TextMessage, projson); err != nil {
  185. panic(err)
  186. }
  187. break
  188. }
  189. }
  190. }()
  191. //设置30秒连接超时,30秒未启动下载则下载失败
  192. go func() {
  193. index :=0
  194. for true{
  195. //启动下载则不做超时判断
  196. if downloading==true{
  197. return
  198. }
  199. index++
  200. time.Sleep(time.Duration(1)*time.Second)
  201. if downloading==false && index==30{
  202. err = cmd.Process.Kill()
  203. log.Println("资源连接超时(30s),下载进程被终止")
  204. if err := conn.WriteMessage(websocket.TextMessage, []byte("-1")); err != nil {
  205. return
  206. }
  207. return
  208. }
  209. }
  210. }()
  211. //等待下载执行完成
  212. err = cmd.Wait()
  213. if err != nil {
  214. log.Printf("cmd.Run() failed with %s\n", err)
  215. }
  216. if errStdout != nil || errStderr != nil {
  217. log.Printf("failed to capture stdout or stderr\n")
  218. }
  219. outStr := string(stdout)
  220. log.Printf("out:%s", outStr)
  221. //更新Etcd数据库的文件key对应hash值
  222. time.Sleep(200*time.Millisecond)
  223. key := gobalLoginUserName+"\\"+projectName+"\\"+nodeDir+"\\"+fileName
  224. err = etcdclient.ReplaceInto(key,hash)
  225. if err != nil {
  226. log.Println(err)
  227. return err
  228. }
  229. //发送消息至文件变更订阅
  230. config.GobalWatchChannelMap[config.LocalWorkSpaceDir+gobalLoginUserName+"\\"+projectName] <- ";"
  231. log.Printf("叮,资源文件[ %v ]下载完成",fileName)
  232. defer close(progress)
  233. return nil
  234. }
  235. //读取解析cmd返回文件上传或下载进度信息
  236. func contentToJSONByte(content string) ([]byte,error){
  237. sts :=strings.Split(content," ")
  238. if len(sts)<8{
  239. log.Println("字符长度小于8")
  240. return nil,nil
  241. }
  242. var processFloat float64
  243. if (len(sts)==9 || len(sts)==8){
  244. processFloat,_ =strconv.ParseFloat(strings.Replace(sts[7],"%","",1), 64)
  245. }else{
  246. processFloat,_ =strconv.ParseFloat(strings.Replace(sts[8],"%","",1), 64)
  247. }
  248. if processFloat==0{
  249. //log.Println("当前进度0")
  250. return nil,nil
  251. }
  252. pro :=&processStruct{
  253. Size:sts[4],
  254. CurrentSize: sts[1],
  255. Unit: sts[2],
  256. CurrentUnit: sts[5],
  257. Process: processFloat,
  258. Hash: "",
  259. }
  260. projson,err :=json.Marshal(pro)
  261. return projson,err
  262. }
  263. /**
  264. 上传本地文件
  265. @param absolutePath 文件本地绝对路径
  266. @param fileName 文件名称
  267. @param projectName 项目名称
  268. @param dir 云文件目录
  269. @param currentHistoryHash 当前文件的历史版本管理文件hash
  270. @param note 备注
  271. @param creator 创建人
  272. @param milestone 是否事里程碑
  273. */
  274. func UploadCommand(conn *websocket.Conn,absolutePath,fileName,projectName,dir,currentHistoryHash,note,creator string,milestone bool) error{
  275. //本地文件目录
  276. fileDir := gobalLocalProjectDir+"\\"+dir
  277. //检查目录
  278. _,err := os.Stat(fileDir)
  279. if err != nil {
  280. //创建文件目录
  281. err = os.MkdirAll(fileDir, os.ModePerm)
  282. if err!=nil{
  283. return err
  284. }
  285. }
  286. serverSh := shell.NewShell(config.ServerIpfsUrl)
  287. //serverSh.SetTimeout(time.Duration(30)*time.Second)
  288. //log.Println("检测引导节点存活情况"+config.ServerIpfsUrl)
  289. //检测引导节点是否连接成功
  290. isUp := serverSh.IsUp()
  291. if !isUp {
  292. log.Println("备份节点网络连接不通!")
  293. if conn==nil{
  294. return errors.New("备份节点连失联")
  295. }else{
  296. if err := conn.WriteMessage(websocket.TextMessage, []byte("-1")); err != nil {
  297. return err
  298. }
  299. }
  300. return nil
  301. }
  302. //上传启动标识,有上传进度则设置为true
  303. var uploading bool=false
  304. //cmd执行ipfs add
  305. cmd := exec.Command(ipfsPath, "add",absolutePath)
  306. uploadProgress := make(chan string,10000)
  307. var stdout, stderr []byte
  308. var errStdout, errStderr error
  309. stdoutIn, _ := cmd.StdoutPipe()
  310. stderrIn, _ := cmd.StderrPipe()
  311. cmd.Start()
  312. go func() {
  313. stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn, uploadProgress)
  314. }()
  315. go func() {
  316. stderr, errStderr = copyAndCapture(os.Stderr, stderrIn, uploadProgress)
  317. }()
  318. //异步给前端反馈上传进度
  319. go func(){
  320. first := true
  321. millSeconds := time.Now().UnixNano() / 1e6
  322. current :=time.Now().UnixNano() / 1e6
  323. for content := range uploadProgress { // 通道关闭后会退出for range循环
  324. current =time.Now().UnixNano() / 1e6
  325. if first {
  326. projson,err := contentToJSONByte(content)
  327. if projson==nil && err==nil{
  328. continue
  329. }
  330. if err != nil {
  331. log.Println("json.Marshal error %s\n", err)
  332. }
  333. //设置上传启动标识为true
  334. uploading=true
  335. if conn!=nil{
  336. if err := conn.WriteMessage(websocket.TextMessage, projson); err != nil {
  337. break
  338. }
  339. }
  340. millSeconds = current
  341. first=false
  342. }
  343. if current-millSeconds>500{
  344. projson,err := contentToJSONByte(content)
  345. if projson==nil && err==nil{
  346. continue
  347. }
  348. if err != nil {
  349. log.Println("json.Marshal error %s\n", err)
  350. }
  351. uploading=true
  352. if conn!=nil{
  353. if err := conn.WriteMessage(websocket.TextMessage, projson); err != nil {
  354. break
  355. }
  356. }
  357. millSeconds = current
  358. }
  359. if (strings.Index(content,"90.00%")!=-1){
  360. projson,err := contentToJSONByte(content)
  361. if projson==nil && err==nil{
  362. continue
  363. }
  364. if err != nil {
  365. log.Println("json.Marshal error %s\n", err)
  366. }
  367. uploading=true
  368. if conn!=nil{
  369. if err := conn.WriteMessage(websocket.TextMessage, projson); err != nil {
  370. break
  371. }
  372. }
  373. break
  374. }
  375. }
  376. }()
  377. log.Println("资源上传中...")
  378. //上传未启动超时时间30s
  379. go func() {
  380. index :=0
  381. for true{
  382. if uploading==true{
  383. return
  384. }
  385. index++
  386. time.Sleep(time.Duration(1)*time.Second)
  387. if uploading==false && index==30{
  388. err = cmd.Process.Kill()
  389. log.Println("资源连接超时(30s),上传进程被终止")
  390. if conn!=nil{
  391. if err := conn.WriteMessage(websocket.TextMessage, []byte("-1")); err != nil {
  392. return
  393. }
  394. }
  395. return
  396. }
  397. }
  398. }()
  399. //等待执行完成
  400. err = cmd.Wait()
  401. if err != nil {
  402. log.Println("cmd.Run() failed with %s\n", err)
  403. if err := conn.WriteMessage(websocket.TextMessage, []byte("-1")); err != nil {
  404. return err
  405. }
  406. return err
  407. }
  408. if errStdout != nil || errStderr != nil {
  409. log.Println("failed to capture stdout or stderr\n")
  410. }
  411. outStr := string(stdout)
  412. fileHash := strings.Split(outStr," ")[1]
  413. log.Printf("out:%s", outStr)
  414. defer close(uploadProgress)
  415. //ipfs provide
  416. cmd = exec.Command(ipfsPath,"dht","provide",fileHash)
  417. err = cmd.Run()
  418. if err != nil {
  419. log.Println(err)
  420. if conn!=nil{
  421. if err := conn.WriteMessage(websocket.TextMessage, []byte("-1")); err != nil {
  422. return err
  423. }
  424. }
  425. return err
  426. }
  427. //判断备份节点对等节点是否已添加连接
  428. sh := shell.NewShell(config.GobalIpfsUrl)
  429. idOut,err :=sh.ID()
  430. localId :=idOut.ID
  431. swarmConnInfos,err :=serverSh.SwarmPeers(context.Background())
  432. hasConnect := false
  433. for _,swarmconn := range swarmConnInfos.Peers {
  434. if swarmconn.Peer==localId{
  435. hasConnect=true
  436. break
  437. }
  438. }
  439. if !hasConnect{
  440. log.Println("中继处理")
  441. swarmConnectAddr :="/ipfs/"+"12D3KooWER8uoGrzeLuJHTXoTMnR4jjHfpcA6ZcpXBaQNJvG5jMP"+"/p2p-circuit/ipfs/"+localId
  442. errT := serverSh.SwarmConnect(context.Background(),swarmConnectAddr)
  443. if errT!=nil{
  444. log.Println("中继失败,引导节点备份失败")
  445. log.Println(err)
  446. if err := conn.WriteMessage(websocket.TextMessage, []byte("-1")); err != nil {
  447. return err
  448. }
  449. }
  450. }
  451. //后台备份
  452. go func() {
  453. err = serverSh.Pin(fileHash)
  454. if err!=nil{
  455. log.Printf("资源[ %v ]备份节点备份失败", fileName)
  456. log.Println(err)
  457. }
  458. log.Printf("资源[ %v ]备份节点备份成功", fileName)
  459. }()
  460. //文件不存在则进行本地文件夹拷贝
  461. if !fileExist(fmt.Sprint((fileDir+"\\"+fileName))) {
  462. //记录新增文件,新增文件不做post请求,前端自行post
  463. goabalAddFileMap[fileName] = 1
  464. err = sh.Get(fileHash,fmt.Sprint((fileDir+"\\"+fileName)))
  465. if err != nil {
  466. log.Println(err)
  467. return err
  468. }
  469. }
  470. //构建历史版本记录、写本地历史版本管理文件,上传至ipfs
  471. filenameall := path.Base(fileName)
  472. filesuffix := path.Ext(fileName)
  473. fileprefix := filenameall[0:len(filenameall) - len(filesuffix)]
  474. commitFilePath := fileDir+"\\"+fileprefix+".commit"
  475. commitVersion,commitHistoryHash,err := commitRecord(commitFilePath,currentHistoryHash,fileHash,note,creator,milestone)
  476. if err != nil {
  477. log.Printf("资源[ %v ]历史版本记录失败", fileName)
  478. log.Println(err)
  479. if conn!=nil{
  480. if err := conn.WriteMessage(websocket.TextMessage, []byte("-1")); err != nil {
  481. return err
  482. }
  483. }
  484. return err
  485. }
  486. //读取文件属性,构建100%进度对象
  487. objectStat,err :=sh.ObjectStat(fileHash)
  488. if err != nil {
  489. log.Println(err)
  490. return err
  491. }
  492. prog := new(processStruct)
  493. prog.Hash=fileHash
  494. prog.Process=100.00
  495. prog.Size=strconv.Itoa(objectStat.CumulativeSize)
  496. prog.CommitHistoryHash=commitHistoryHash
  497. projson,err :=json.Marshal(prog)
  498. if conn!=nil{
  499. if err := conn.WriteMessage(websocket.TextMessage, projson); err != nil {
  500. log.Println(err)
  501. return err
  502. }
  503. }
  504. //更新Etcd数据库hash
  505. key := gobalLoginUserName+"\\"+projectName+"\\"+dir+"\\"+fileName
  506. err = etcdclient.ReplaceInto(key,prog.Hash+";0")
  507. if err != nil {
  508. log.Println(err)
  509. return err
  510. }
  511. if conn==nil{
  512. folderName := strings.Split(dir,"\\")[0]
  513. var relativePath string
  514. if len(strings.Split(dir, "\\"))==1{
  515. relativePath = ""
  516. }else{
  517. relativePath = strings.Replace(relativePath, folderName+"\\","",1)
  518. }
  519. size,_ := strconv.ParseInt(prog.Size,10,64)
  520. err = postUpdateFile(projectName,folderName,relativePath,fileHash, fileName, commitHistoryHash, currentHistoryHash, gobalLoginUserId, size, commitVersion)
  521. if err!=nil{
  522. return err
  523. }
  524. }
  525. //发送文件至文件变更订阅
  526. config.GobalWatchChannelMap[config.LocalWorkSpaceDir+gobalLoginUserName+"\\"+projectName] <- ";"
  527. log.Printf("叮,资源文件[ %v ]上传完成",fileName)
  528. return nil
  529. }
  530. //文件信息入参
  531. type FileParam struct {
  532. Digest string `json:"digest"` //md5(ProjectName|FolderName|RelativePath|FileName|FileVersion|UserName)
  533. ProjectName string `json:"projectName"`
  534. FolderName string `json:"folderName"`
  535. RelativePath string `json:"relativePath"`
  536. IpfsCid string `json:"ipfsCid"`
  537. FileName string `json:"fileName"`
  538. FileSize int64 `json:"fileSize,string"`
  539. FileVersion int `json:"fileVersion"`
  540. UserId int64 `json:"userId,string"`
  541. HistoryCurrentIpfsCid string `json:"historyCurrentIpfsCid"`
  542. HistoryPreIpfsCid string `json:"historyPreIpfsCid"`
  543. }
  544. //获取文件的历史版本管理文件最新hash值
  545. func postGetHistoryCurrentHash(projectName, folderName, fileName, relativePath string)(string,error){
  546. url:="http://www.lockingos.org:9003/api/pms/sdk/queryFileHistoryCurrentCid"
  547. contentType := "application/json"
  548. fileParam :=FileParam{
  549. ProjectName: projectName,
  550. FolderName: folderName,
  551. RelativePath: relativePath,
  552. FileName: fileName,
  553. IpfsCid: "",
  554. FileSize: 0,
  555. FileVersion: 0,
  556. UserId: 0,
  557. HistoryPreIpfsCid: "",
  558. HistoryCurrentIpfsCid: "",
  559. }
  560. text:=fmt.Sprintf("%v|%v|%v|%v|%v|%v|%v|%v|%v|%v",projectName,folderName,relativePath,fileParam.IpfsCid,fileName,fileParam.FileSize,fileParam.FileVersion,fileParam.UserId,fileParam.HistoryCurrentIpfsCid,fileParam.HistoryPreIpfsCid)
  561. textByte := []byte(text)
  562. md5Byte := md5.Sum(textByte)
  563. digest := fmt.Sprintf("%x", md5Byte)
  564. fileParam.Digest=digest
  565. jsonData,err :=json.Marshal(fileParam)
  566. if err!=nil{
  567. log.Printf("json序列化化错误!")
  568. return "",err
  569. }
  570. //log.Print(string(jsonData[:]))
  571. resp, err := http.Post(url, contentType, bytes.NewReader(jsonData))
  572. if err != nil {
  573. fmt.Println("post failed, err:%v\n", err)
  574. return "",err
  575. }
  576. defer resp.Body.Close()
  577. b, err := ioutil.ReadAll(resp.Body)
  578. if err != nil {
  579. fmt.Println("get resp failed,err:%v\n", err)
  580. return "",err
  581. }
  582. //log.Printf("post response:%v", string(b))
  583. result := make(map[string] interface{})
  584. err=json.Unmarshal(b,&result)
  585. if err!=nil{
  586. log.Printf("字符串%v反序列化出错", string(b[:]))
  587. }
  588. if result["Msg"].(string)==""{
  589. log.Printf("资源[ %v ]历史版本管理文件Hash:%v",fileName,result["Data"].(string))
  590. return result["Data"].(string),nil
  591. }
  592. return "",errors.New(result["Msg"].(string))
  593. }
  594. //更新文件记录
  595. func postUpdateFile(projectName, folderName, relativePath, ipfsCid, fileName, historyCurrentIpfsCid, historyPreIpfsCid,userId string, fileSize int64, fileVersion int)(err error){
  596. url:="http://www.lockingos.org:9003/api/pms/sdk/updateFile"
  597. contentType := "application/json"
  598. intUserId,_:=strconv.ParseInt(userId,10,64)
  599. fileParam :=FileParam{
  600. ProjectName: projectName,
  601. FolderName: folderName,
  602. RelativePath: relativePath,
  603. IpfsCid: ipfsCid,
  604. FileName: fileName,
  605. FileSize: fileSize,
  606. FileVersion: fileVersion,
  607. UserId: intUserId,
  608. HistoryCurrentIpfsCid: historyCurrentIpfsCid,
  609. HistoryPreIpfsCid: historyPreIpfsCid,
  610. }
  611. text:=fmt.Sprintf("%v|%v|%v|%v|%v|%v|%v|%v|%v|%v",projectName,folderName,relativePath,ipfsCid,fileName,fileSize,fileVersion,userId,historyCurrentIpfsCid,historyPreIpfsCid)
  612. textByte := []byte(text)
  613. md5Byte := md5.Sum(textByte)
  614. digest := fmt.Sprintf("%x", md5Byte)
  615. fileParam.Digest=digest
  616. jsonData,err :=json.Marshal(fileParam)
  617. if err!=nil{
  618. log.Printf("json序列化化错误!")
  619. return err
  620. }
  621. resp, err := http.Post(url, contentType, bytes.NewReader(jsonData))
  622. if err != nil {
  623. fmt.Println("post failed, err:%v\n", err)
  624. return err
  625. }
  626. defer resp.Body.Close()
  627. b, err := ioutil.ReadAll(resp.Body)
  628. if err != nil {
  629. fmt.Println("get resp failed,err:%v\n", err)
  630. return err
  631. }
  632. //log.Printf("post response:%v", string(b))
  633. result := make(map[string] interface{})
  634. err=json.Unmarshal(b,&result)
  635. if err!=nil{
  636. log.Printf("字符串%v反序列化出错", string(b[:]))
  637. return err
  638. }
  639. log.Println("资源[ %v ]服务记录更新成功",fileName)
  640. return nil
  641. }
  642. /**
  643. 记录提交记录
  644. */
  645. func commitRecord(path,currentHistoryHash,hash,note,creator string, milestone bool) (int,string,error){
  646. commitHistory := new(commitHistory)
  647. //历史文件不存在则创建
  648. localSh :=shell.NewShell(config.GobalIpfsUrl)
  649. localSh.SetTimeout(10*time.Second)
  650. if len(currentHistoryHash)!=0 {
  651. os.Remove(path)
  652. err := localSh.Get(currentHistoryHash,path)
  653. if err != nil {
  654. log.Println("历史版本管理文件下载失败")
  655. return -1,"",err
  656. }
  657. }
  658. //初始化历史管理文件
  659. exist := fileExist(path)
  660. if !exist {
  661. commitFile,err := os.Create(path)
  662. if err != nil {
  663. log.Println("历史版本管理文件创建失败")
  664. return -1,"",err
  665. }
  666. commitFile.Close()
  667. }
  668. //设置文件隐藏属性
  669. attribCmd :=exec.Command("attrib","+h",path)
  670. err :=attribCmd.Run()
  671. if err != nil {
  672. log.Println("设置文件隐藏属性失败")
  673. return -1,"",err
  674. }
  675. //读取历史管理文件
  676. content ,err :=ioutil.ReadFile(path)
  677. if len(content)!=0{
  678. rows :=strings.Split(string(content),"\n")
  679. endRow :=rows[len(rows)-2]
  680. columns :=strings.Split(endRow,"\t")
  681. commitHistory.Version,_=strconv.Atoi(columns[6])
  682. commitHistory.Version++
  683. commitHistory.ParentHash=columns[1]
  684. }else{
  685. commitHistory.Version=1
  686. commitHistory.ParentHash="0000000000000000000000000000000000000000"
  687. }
  688. commitHistory.CurrentHash=hash
  689. commitHistory.Milestone = milestone
  690. commitHistory.Creator = creator
  691. commitHistory.Note = note
  692. commitHistory.CreateTime=time.Now().Unix()
  693. if commitHistory.ParentHash==commitHistory.CurrentHash{
  694. return commitHistory.Version,commitHistory.CurrentHash,nil
  695. }
  696. file,err :=os.OpenFile(path,os.O_APPEND,0666)
  697. if err != nil{
  698. log.Println(err)
  699. return -1,"",err
  700. }
  701. //写入历史管理文件
  702. w :=bufio.NewWriter(file)
  703. writeContent:=fmt.Sprintf("%v\t%v\t%v\t%v\t%v\t%v\t%v\t",commitHistory.ParentHash,commitHistory.CurrentHash,commitHistory.Creator,commitHistory.CreateTime,commitHistory.Note,commitHistory.Milestone,commitHistory.Version)
  704. fmt.Fprintln(w,writeContent)
  705. err = w.Flush()
  706. if err != nil {
  707. log.Println("历史版本管理文件写入失败")
  708. return -1,"",err
  709. }
  710. file.Close()
  711. addFile,err :=os.Open(path)
  712. if err != nil{
  713. log.Println(err)
  714. return -1,"",err
  715. }
  716. defer addFile.Close()
  717. //add 历史管理文件
  718. historyHash,err:=localSh.Add(addFile)
  719. if err != nil {
  720. log.Println("历史版本管理文件上传失败")
  721. return -1,"",err
  722. }
  723. serverSh :=shell.NewShell(config.ServerIpfsUrl)
  724. serverSh.SetTimeout(30*time.Second)
  725. err = serverSh.Pin(historyHash)
  726. if err != nil {
  727. log.Println("历史版本管理文件备份失败")
  728. return -1,"",err
  729. }
  730. return commitHistory.Version,historyHash,nil
  731. }
  732. func copyAndCapture(w io.Writer, r io.Reader, progress chan string) ([]byte, error) {
  733. var out []byte
  734. buf := make([]byte, 1024, 1024)
  735. for {
  736. n, err := r.Read(buf[:])
  737. if n > 0 {
  738. d := buf[:n]
  739. out = append(out, d...)
  740. progress <- string(d)
  741. }
  742. if err != nil {
  743. // Read returns io.EOF at the end of file, which is not an error for us
  744. if err == io.EOF {
  745. err = nil
  746. }
  747. return out, err
  748. }
  749. }
  750. // never reached
  751. panic(true)
  752. return nil, nil
  753. }
  754. /**
  755. 单个文件信息
  756. */
  757. type simpleFileInfo struct {
  758. Name string `json:"name" `
  759. Extension string `json:"extension"`
  760. RelativePath string `json:"relativePath"`
  761. AbsolutePath string `json:"absolutePath"`
  762. }
  763. var gobalFolderFileMap map[string] *simpleFileInfo
  764. var gobalRelativePath string
  765. /**
  766. 获取指定目录或文件的文件信息,如果是目录递归获取文件信息
  767. @param id 文件id
  768. */
  769. func GetFolderFileInfo(conn *websocket.Conn,absolutePath string) error{
  770. gobalFolderFileMap = make(map[string] *simpleFileInfo)
  771. fileInfo,err :=os.Stat(absolutePath)
  772. if err!=nil{
  773. log.Println(err)
  774. return err
  775. }
  776. log.Println(filepath.Dir(absolutePath))
  777. //单个文件处理
  778. if !fileInfo.IsDir() {
  779. simpleFileInfo := new(simpleFileInfo)
  780. simpleFileInfo.Name=fileInfo.Name()
  781. simpleFileInfo.Extension=path.Ext(absolutePath)
  782. simpleFileInfo.RelativePath=""
  783. simpleFileInfo.AbsolutePath=absolutePath
  784. gobalFolderFileMap[absolutePath]=simpleFileInfo
  785. bytes,err :=json.Marshal(gobalFolderFileMap)
  786. if err != nil {
  787. log.Println(err)
  788. return err
  789. }
  790. if err := conn.WriteMessage(websocket.TextMessage, bytes); err != nil {
  791. log.Println(err)
  792. return err
  793. }
  794. return nil
  795. }
  796. //文件目录处理
  797. gobalRelativePath = filepath.Dir(absolutePath)
  798. err =filepath.Walk(absolutePath, myWalkfunc)
  799. if err != nil {
  800. log.Println(err)
  801. return err
  802. }
  803. bytes,err :=json.Marshal(gobalFolderFileMap)
  804. if err != nil {
  805. log.Println(err)
  806. return err
  807. }
  808. if err := conn.WriteMessage(websocket.TextMessage, bytes); err != nil {
  809. log.Println(err)
  810. return err
  811. }
  812. return nil
  813. }
  814. func myWalkfunc(path string, info os.FileInfo, err error) error {
  815. if info.IsDir()==false{
  816. simpleFileInfo := new(simpleFileInfo)
  817. simpleFileInfo.Name=info.Name()
  818. simpleFileInfo.Extension=filepath.Ext(path)
  819. simpleFileInfo.RelativePath=filepath.Dir(strings.Replace(path,gobalRelativePath,"",1))
  820. simpleFileInfo.AbsolutePath=path
  821. gobalFolderFileMap[path]=simpleFileInfo
  822. return nil
  823. }
  824. return nil
  825. }
  826. /**
  827. 本地文件是否存在
  828. */
  829. func fileExist(path string) bool {
  830. _, err := os.Lstat(path)
  831. return !os.IsNotExist(err)
  832. }
  833. /**
  834. 获取本地文件列表
  835. */
  836. func SubscriptionFileChange(conn *websocket.Conn, projectName string) error{
  837. projectPath := gobalLocalProjectDir
  838. //log.Println("切换文件列表:"+projectPath)
  839. keyPrefix := gobalLoginUserName+"\\"+projectName+"\\"
  840. //添加监控
  841. err := filepath.Walk(projectPath,watchWalkfunc)
  842. if err != nil {
  843. log.Println(err)
  844. return err
  845. }
  846. //初始化通道
  847. if config.GobalWatchChannelMap[projectPath] != nil {
  848. close(config.GobalWatchChannelMap[projectPath])
  849. }
  850. config.GobalWatchChannelMap[projectPath]=make(chan string,100)
  851. log.Println("添加文件监控:"+projectPath)
  852. //定期校验缓存的本地文件状态
  853. dataMapa,err := etcdclient.QueryWithPrefix(keyPrefix)
  854. if err != nil {
  855. log.Println(err)
  856. }
  857. if dataMapa!=nil && len(dataMapa)>0{
  858. for k,_ := range dataMapa {
  859. if !fileExist(config.LocalWorkSpaceDir+k){
  860. err = etcdclient.DeleteWithPrefix(k)
  861. if err != nil {
  862. log.Println(err)
  863. }
  864. }
  865. }
  866. }
  867. //优先etcd查询
  868. dataMap,err := etcdclient.QueryWithPrefix(keyPrefix)
  869. if err != nil {
  870. log.Println(err)
  871. return err
  872. }
  873. if dataMap==nil || len(dataMap)==0{
  874. // 不存在则初始化进etcd
  875. err =filepath.Walk(gobalLocalProjectDir,walkfunc)
  876. //路径错误
  877. if err != nil {
  878. log.Println(err)
  879. if err := conn.WriteMessage(websocket.TextMessage, []byte("{}")); err != nil {
  880. log.Println(err)
  881. return err
  882. }
  883. }
  884. mapByte,err:=json.Marshal(gobalFileMap)
  885. if err != nil {
  886. log.Println(err)
  887. return err
  888. }
  889. if err := conn.WriteMessage(websocket.TextMessage, mapByte); err != nil {
  890. log.Println(err)
  891. return err
  892. }
  893. cacheMap := make(map[string] string)
  894. for k,v := range gobalFileMap {
  895. k := strings.Replace(k,config.LocalWorkSpaceDir,"",1)
  896. cacheMap[k]=v
  897. }
  898. //异步缓存
  899. //go func() {
  900. err = etcdclient.BatchAdd(cacheMap)
  901. if err != nil {
  902. log.Println(err)
  903. }
  904. //}()
  905. //log.Println("检查本地目录文件变更状态,执行睡眠时间1分钟")
  906. //清空gobalFileMap
  907. gobalFileMap = make(map[string] string)
  908. }
  909. err=sendFileListFromEtcd(keyPrefix,projectName,conn)
  910. if err != nil {
  911. log.Println(err)
  912. return err
  913. }
  914. for actionAndModifyFilePathStr :=range config.GobalWatchChannelMap[gobalLocalProjectDir] {
  915. //log.Println(actionAndModifyFilePathStr)
  916. actionAndModifyFilePath := strings.Split(actionAndModifyFilePathStr,";")
  917. if actionAndModifyFilePath[0]=="remove"{
  918. k := strings.Replace(actionAndModifyFilePath[1],config.LocalWorkSpaceDir,"",1)
  919. queryMap,err :=etcdclient.QueryWithPrefix(k)
  920. if len(queryMap)==0{
  921. continue
  922. }
  923. err = etcdclient.DeleteWithPrefix(k)
  924. if err != nil {
  925. log.Println(err)
  926. }
  927. }else if actionAndModifyFilePath[0]=="write"{
  928. queryKey := strings.Replace(actionAndModifyFilePath[1],config.LocalWorkSpaceDir,"",1)
  929. querymap,err := etcdclient.QueryWithPrefix(queryKey)
  930. if err != nil {
  931. log.Println(err)
  932. continue
  933. }
  934. if len(querymap)==0{
  935. continue
  936. }
  937. oldValue := strings.Split(querymap[queryKey],";")
  938. newValue := oldValue[0]+";" +"1"
  939. err = etcdclient.ReplaceInto(queryKey,newValue)
  940. if err!=nil{
  941. log.Println(err)
  942. continue
  943. }
  944. log.Printf("文件变更 [ %v ] write", actionAndModifyFilePathStr)
  945. //如果非新增文件则自动post
  946. //if goabalAddFileMap[]
  947. filePath := actionAndModifyFilePath[1]
  948. //TODO
  949. //获取文件的历史版本管理文件hash
  950. fileName :=filepath.Base(filePath)
  951. folderName := strings.Split(queryKey,"\\")[2]
  952. dir := strings.Replace(strings.Replace(queryKey,"\\"+fileName,"",1),strings.Split(queryKey,"\\")[0]+"\\"+strings.Split(queryKey,"\\")[1]+"\\","",1)
  953. var relativePath string
  954. if len(strings.Split(dir, "\\"))==1{
  955. relativePath = ""
  956. }else{
  957. relativePath = strings.Replace(relativePath, folderName+"\\","",1)
  958. }
  959. historyCurrentHash,err :=postGetHistoryCurrentHash(projectName, folderName, fileName, relativePath)
  960. if err!=nil{
  961. log.Printf("postGetHistoryCurrentHash 返回失败,%v",err.Error())
  962. continue
  963. }
  964. //自动更新文件
  965. err =UploadCommand(nil,filePath,fileName,projectName,dir,historyCurrentHash,"",gobalLoginUserName,false)
  966. if err!=nil{
  967. log.Printf("UploadCommand 返回失败,%v",err.Error())
  968. //记录修改状态
  969. newValue := oldValue[0]+";" +"1"
  970. err = etcdclient.ReplaceInto(queryKey,newValue)
  971. if err!=nil{
  972. log.Println(err)
  973. continue
  974. }
  975. continue
  976. }
  977. }else if actionAndModifyFilePath[0]=="create"{
  978. queryKey := strings.Replace(actionAndModifyFilePath[1],config.LocalWorkSpaceDir,"",1)
  979. querymap,err := etcdclient.QueryWithPrefix(queryKey)
  980. if err != nil {
  981. log.Println(err)
  982. continue
  983. }
  984. if len(querymap)==0{
  985. continue
  986. }
  987. oldValue := strings.Split(querymap[queryKey],";")
  988. newValue := oldValue[0]+";" +"1"
  989. err = etcdclient.ReplaceInto(queryKey,newValue)
  990. if err!=nil{
  991. log.Println(err)
  992. continue
  993. }
  994. log.Printf("文件变更 [ %v ] create", actionAndModifyFilePathStr)
  995. //如果非新增文件则自动post
  996. //if goabalAddFileMap[]
  997. filePath := actionAndModifyFilePath[1]
  998. //TODO
  999. //获取文件的历史版本管理文件hash
  1000. fileName :=filepath.Base(filePath)
  1001. folderName := strings.Split(queryKey,"\\")[2]
  1002. dir := strings.Replace(strings.Replace(queryKey,"\\"+fileName,"",1),strings.Split(queryKey,"\\")[0]+"\\"+strings.Split(queryKey,"\\")[1]+"\\","",1)
  1003. var relativePath string
  1004. if len(strings.Split(dir, "\\"))==1{
  1005. relativePath = ""
  1006. }else{
  1007. relativePath = strings.Replace(relativePath, folderName+"\\","",1)
  1008. }
  1009. historyCurrentHash,err :=postGetHistoryCurrentHash(projectName, folderName, fileName, relativePath)
  1010. if err!=nil{
  1011. log.Printf("postGetHistoryCurrentHash 返回失败,%v",err.Error())
  1012. continue
  1013. }
  1014. //自动更新文件
  1015. err =UploadCommand(nil,filePath,fileName,projectName,dir,historyCurrentHash,"",gobalLoginUserName,false)
  1016. if err!=nil{
  1017. log.Printf("UploadCommand 返回失败,%v",err.Error())
  1018. //记录修改状态
  1019. newValue := oldValue[0]+";" +"1"
  1020. err = etcdclient.ReplaceInto(queryKey,newValue)
  1021. if err!=nil{
  1022. log.Println(err)
  1023. continue
  1024. }
  1025. continue
  1026. }
  1027. }
  1028. err = sendFileListFromEtcd(keyPrefix,projectName,conn)
  1029. if err != nil {
  1030. log.Println(err)
  1031. return err
  1032. }
  1033. }
  1034. return nil
  1035. }
  1036. func sendFileListFromEtcd(keyPrefix,projectName string,conn *websocket.Conn) error{
  1037. dataMap,err := etcdclient.QueryWithPrefix(keyPrefix)
  1038. if err != nil {
  1039. log.Println(err)
  1040. return err
  1041. }
  1042. if dataMap!=nil && len(dataMap)>0{
  1043. cacheMap := make(map[string] string)
  1044. for k,v := range dataMap {
  1045. //历史数据加默认值
  1046. if len(strings.Split(v, ";"))==1{
  1047. v=v+";0"
  1048. }
  1049. cacheMap[strings.Replace(k,gobalLoginUserName+"\\"+projectName+"\\","",1)]=v
  1050. }
  1051. mapByte,err:=json.Marshal(cacheMap)
  1052. if err != nil {
  1053. log.Println(err)
  1054. return err
  1055. }
  1056. if err := conn.WriteMessage(websocket.TextMessage, mapByte); err != nil {
  1057. log.Println(err)
  1058. return err
  1059. }
  1060. return nil
  1061. }else{
  1062. log.Println("未查询到数据,keyPrefix:"+keyPrefix+",projectName:"+projectName)
  1063. }
  1064. return nil
  1065. }
  1066. /**
  1067. 打开方式
  1068. */
  1069. func OpenFileWith(filePath string) error{
  1070. //判断文件有效性
  1071. _,err := os.Stat(filePath)
  1072. if err!=nil{
  1073. return err
  1074. }
  1075. //filePath = strings.Replace(filePath," ","~1",1)
  1076. cmd := exec.Command("rundll32.exe","shell32.dll,OpenAs_RunDLL",filePath);
  1077. err =cmd.Run()
  1078. if err!=nil{
  1079. log.Println(err)
  1080. return err
  1081. }
  1082. return nil
  1083. }
  1084. /**
  1085. 手动检查软件更新
  1086. 0:不强制更新
  1087. 1:强制更新
  1088. */
  1089. func CheckForUpdates(forceUpdate string) error{
  1090. tszdir :=os.Getenv("TSZDIR")
  1091. //空格路径处理
  1092. ipfsPath=strings.Replace(os.Getenv("IPFS-PATH"),"\"","",1)+"\\stop.vbs"
  1093. //判断文件有效性
  1094. _,err := os.Stat(tszdir+config.UpdaterName)
  1095. if err!=nil{
  1096. return err
  1097. }
  1098. cmd := exec.Command(tszdir+config.UpdaterName,"/justcheck");
  1099. err =cmd.Run()
  1100. if err!=nil{
  1101. log.Println(err)
  1102. return err
  1103. }
  1104. cmd = exec.Command(tszdir+config.UpdaterName,"/checknow");
  1105. err =cmd.Run()
  1106. if err!=nil{
  1107. log.Println(err)
  1108. return err
  1109. }
  1110. //检测到更新 kill所有客户端进程
  1111. log.Println("close all process")
  1112. cmd = exec.Command("cmd.exe","/c",ipfsPath);
  1113. err =cmd.Run()
  1114. if err!=nil{
  1115. log.Println(err)
  1116. return err
  1117. }
  1118. return nil
  1119. }
  1120. func walkfunc(filePath string, info os.FileInfo, err error) error {
  1121. if info == nil{
  1122. return nil
  1123. }
  1124. if info.IsDir()==false{
  1125. //历史文件不扫描
  1126. if path.Ext(filePath)==".commit" {
  1127. return nil
  1128. }
  1129. sh := shell.NewShell(config.GobalIpfsUrl)
  1130. file,err :=os.Open(filePath)
  1131. if err != nil{
  1132. log.Println(err)
  1133. return err
  1134. }
  1135. defer file.Close()
  1136. hash,err :=sh.Add(file)
  1137. if err != nil {
  1138. log.Println(err)
  1139. return err
  1140. }
  1141. dir :=strings.Replace(fmt.Sprint(filePath),fmt.Sprint(gobalLocalProjectDir+"\\"),"",1)
  1142. gobalFileMap[dir]=hash
  1143. }
  1144. return nil
  1145. }
  1146. /**
  1147. 查询历史文件
  1148. path 文件路径
  1149. hash 历史版本文件hash
  1150. */
  1151. func QueryCommitHistory(filePath,hash string) (map[int] *commitHistory,error){
  1152. result := make(map[int] *commitHistory)
  1153. //校验文件路径
  1154. _,err :=os.Stat(filePath)
  1155. if err != nil {
  1156. log.Println("文件 "+filePath+"not found")
  1157. return nil,errors.New("参数错误!")
  1158. }
  1159. if len(hash) == 0 {
  1160. return result,nil
  1161. }
  1162. //根据hash更新文件
  1163. localSh := shell.NewShell(config.GobalIpfsUrl)
  1164. //连接失败判断
  1165. localSh.SetTimeout(5*time.Second)
  1166. ext :=path.Ext(filePath)
  1167. commitFilePath :=strings.Replace(filePath,ext,".commit",1)
  1168. os.Remove(commitFilePath)
  1169. err = localSh.Get(hash,commitFilePath)
  1170. if err!=nil {
  1171. log.Println("文件"+hash+"下载失败")
  1172. return result,errors.New("历史文件获取失败,请稍后重试")
  1173. }
  1174. //设置文件隐藏属性
  1175. attribCmd :=exec.Command("attrib","+h",commitFilePath)
  1176. err =attribCmd.Run()
  1177. if err != nil {
  1178. log.Println("设置文件隐藏属性失败")
  1179. return result,err
  1180. }
  1181. //解析历史版本文件
  1182. contentByte,err := ioutil.ReadFile(commitFilePath)
  1183. content := string(contentByte)
  1184. if content==""{
  1185. return result,nil
  1186. }
  1187. rows :=strings.Split(content,"\n")
  1188. length := len(rows)
  1189. var index int = 0
  1190. for i:=length-2;i>=0;i--{
  1191. columns := strings.Split(rows[i],"\t")
  1192. commitHistoryInstance := new(commitHistory)
  1193. commitHistoryInstance.ParentHash =columns[0]
  1194. commitHistoryInstance.CurrentHash=columns[1]
  1195. commitHistoryInstance.Version,_=strconv.Atoi(columns[6])
  1196. commitHistoryInstance.Milestone,_=strconv.ParseBool(columns[5])
  1197. commitHistoryInstance.Creator=columns[2]
  1198. commitHistoryInstance.CreateTime,_=strconv.ParseInt(columns[3], 10, 64)
  1199. commitHistoryInstance.Note = columns[4]
  1200. result[index]=commitHistoryInstance
  1201. index++
  1202. }
  1203. return result,nil
  1204. }
  1205. /**
  1206. 设定某个历史版本为里程碑版本
  1207. @param filePath 文件绝对路径
  1208. @param commitHistoryHash 历史版本管理文件hash
  1209. @param hash 文件hash
  1210. @param milestone 是否是里程碑
  1211. */
  1212. func EditCommitHistoryMilestoneHandler(filePath,commitHistoryHash,hash string,milestone bool) (string,error){
  1213. //result := make(map[int] *commitHistory)
  1214. //校验文件路径
  1215. _,err :=os.Stat(filePath)
  1216. if err != nil {
  1217. log.Println("文件 "+filePath+"not found")
  1218. return "",errors.New("参数错误!")
  1219. }
  1220. if len(commitHistoryHash) == 0 {
  1221. log.Println("参数hash must not empty")
  1222. return "",errors.New("参数错误!")
  1223. }
  1224. //根据hash更新文件
  1225. localSh := shell.NewShell(config.GobalIpfsUrl)
  1226. //连接失败判断
  1227. localSh.SetTimeout(5*time.Second)
  1228. ext :=path.Ext(filePath)
  1229. commitFilePath :=strings.Replace(filePath,ext,".commit",1)
  1230. os.Remove(commitFilePath)
  1231. err = localSh.Get(commitHistoryHash,commitFilePath)
  1232. if err!=nil {
  1233. log.Println("文件"+commitHistoryHash+"下载失败")
  1234. return "",errors.New("历史文件获取失败,请稍后重试")
  1235. }
  1236. //设置文件隐藏属性
  1237. attribCmd :=exec.Command("attrib","+h",commitFilePath)
  1238. err =attribCmd.Run()
  1239. if err != nil {
  1240. log.Println("设置文件隐藏属性失败")
  1241. return "",err
  1242. }
  1243. //解析历史版本文件
  1244. contentByte,err := ioutil.ReadFile(commitFilePath)
  1245. content := string(contentByte)
  1246. if content==""{
  1247. return "",nil
  1248. }
  1249. rows :=strings.Split(content,"\n")
  1250. length := len(rows)
  1251. resultString :=""
  1252. for i:=0;i<length-1;i++{
  1253. columns := strings.Split(rows[i],"\t")
  1254. if columns[1]==hash{
  1255. resultString=resultString+fmt.Sprintf("%v\t%v\t%v\t%v\t%v\t%v\t%v\t\n",columns[0],columns[1],columns[2],columns[3],columns[4],milestone,columns[6])
  1256. continue
  1257. }
  1258. resultString=resultString+fmt.Sprintf("%v\t%v\t%v\t%v\t%v\t%v\t%v\t\n",columns[0],columns[1],columns[2],columns[3],columns[4],columns[5],columns[6])
  1259. }
  1260. os.Remove(commitFilePath)
  1261. fw, err := os.OpenFile(commitFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)//os.O_TRUNC清空文件重新写入,否则原文件内容可能残留
  1262. w := bufio.NewWriter(fw)
  1263. w.WriteString(resultString)
  1264. if err != nil {
  1265. log.Println(err)
  1266. return "",err
  1267. }
  1268. w.Flush()
  1269. fw.Close()
  1270. addFile,err :=os.Open(commitFilePath)
  1271. if err != nil{
  1272. log.Println(err)
  1273. return "",err
  1274. }
  1275. defer addFile.Close()
  1276. //add 历史管理文件
  1277. historyHash,err:=localSh.Add(addFile)
  1278. if err != nil {
  1279. log.Println("历史版本管理文件上传失败")
  1280. return "",err
  1281. }
  1282. serverSh :=shell.NewShell(config.ServerIpfsUrl)
  1283. serverSh.SetTimeout(5*time.Second)
  1284. err = serverSh.Pin(historyHash)
  1285. if err != nil {
  1286. log.Println("历史版本管理文件备份失败")
  1287. return "",err
  1288. }
  1289. return historyHash,nil
  1290. }
  1291. /*
  1292. 提交历史
  1293. */
  1294. type commitHistory struct {
  1295. ParentHash string `json:"parentHash"`
  1296. CurrentHash string `json:"currentHash"`
  1297. Creator string `json:"creator"`
  1298. CreateTime int64 `json:"createTime"`
  1299. Note string `json:"note"`
  1300. Version int `json:"version"`
  1301. Milestone bool `json:"milestone"`
  1302. }