file.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // SiYuan - Build Your Eternal Digital Garden
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package api
  17. import (
  18. "io"
  19. "mime/multipart"
  20. "net/http"
  21. "os"
  22. "path/filepath"
  23. "strconv"
  24. "time"
  25. "github.com/88250/gulu"
  26. "github.com/gin-gonic/gin"
  27. "github.com/siyuan-note/filelock"
  28. "github.com/siyuan-note/logging"
  29. "github.com/siyuan-note/siyuan/kernel/model"
  30. "github.com/siyuan-note/siyuan/kernel/util"
  31. )
  32. func copyFile(c *gin.Context) {
  33. ret := gulu.Ret.NewResult()
  34. defer c.JSON(http.StatusOK, ret)
  35. arg, ok := util.JsonArg(c, ret)
  36. if !ok {
  37. return
  38. }
  39. src := arg["src"].(string)
  40. src, err := model.GetAssetAbsPath(src)
  41. if nil != err {
  42. logging.LogErrorf("get asset [%s] abs path failed: %s", src, err)
  43. ret.Code = -1
  44. ret.Msg = err.Error()
  45. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  46. return
  47. }
  48. info, err := os.Stat(src)
  49. if nil != err {
  50. logging.LogErrorf("stat [%s] failed: %s", src, err)
  51. ret.Code = -1
  52. ret.Msg = err.Error()
  53. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  54. return
  55. }
  56. if info.IsDir() {
  57. ret.Code = -1
  58. ret.Msg = "file is a directory"
  59. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  60. return
  61. }
  62. dest := arg["dest"].(string)
  63. if err = filelock.Copy(src, dest); nil != err {
  64. logging.LogErrorf("copy file [%s] to [%s] failed: %s", src, dest, err)
  65. ret.Code = -1
  66. ret.Msg = err.Error()
  67. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  68. return
  69. }
  70. }
  71. func getFile(c *gin.Context) {
  72. ret := gulu.Ret.NewResult()
  73. arg, ok := util.JsonArg(c, ret)
  74. if !ok {
  75. c.JSON(http.StatusOK, ret)
  76. return
  77. }
  78. filePath := arg["path"].(string)
  79. filePath = filepath.Join(util.WorkspaceDir, filePath)
  80. info, err := os.Stat(filePath)
  81. if os.IsNotExist(err) {
  82. c.Status(404)
  83. return
  84. }
  85. if nil != err {
  86. logging.LogErrorf("stat [%s] failed: %s", filePath, err)
  87. c.Status(500)
  88. return
  89. }
  90. if info.IsDir() {
  91. logging.LogErrorf("file [%s] is a directory", filePath)
  92. c.Status(405)
  93. return
  94. }
  95. if err = model.ServeFile(c, filePath); nil != err {
  96. c.Status(http.StatusConflict)
  97. return
  98. }
  99. }
  100. func putFile(c *gin.Context) {
  101. ret := gulu.Ret.NewResult()
  102. defer c.JSON(http.StatusOK, ret)
  103. filePath := c.PostForm("path")
  104. filePath = filepath.Join(util.WorkspaceDir, filePath)
  105. isDirStr := c.PostForm("isDir")
  106. isDir, _ := strconv.ParseBool(isDirStr)
  107. var err error
  108. if isDir {
  109. err = os.MkdirAll(filePath, 0755)
  110. if nil != err {
  111. logging.LogErrorf("make a dir [%s] failed: %s", filePath, err)
  112. }
  113. } else {
  114. fileHeader, _ := c.FormFile("file")
  115. if nil == fileHeader {
  116. logging.LogErrorf("form file is nil [path=%s]", filePath)
  117. c.Status(400)
  118. return
  119. }
  120. for {
  121. dir := filepath.Dir(filePath)
  122. if err = os.MkdirAll(dir, 0755); nil != err {
  123. logging.LogErrorf("put a file [%s] make dir [%s] failed: %s", filePath, dir, err)
  124. break
  125. }
  126. var f multipart.File
  127. f, err = fileHeader.Open()
  128. if nil != err {
  129. logging.LogErrorf("open file failed: %s", err)
  130. break
  131. }
  132. var data []byte
  133. data, err = io.ReadAll(f)
  134. if nil != err {
  135. logging.LogErrorf("read file failed: %s", err)
  136. break
  137. }
  138. err = filelock.WriteFile(filePath, data)
  139. if nil != err {
  140. logging.LogErrorf("put a file [%s] failed: %s", filePath, err)
  141. break
  142. }
  143. break
  144. }
  145. }
  146. if nil != err {
  147. ret.Code = -1
  148. ret.Msg = err.Error()
  149. return
  150. }
  151. modTimeStr := c.PostForm("modTime")
  152. modTimeInt, err := strconv.ParseInt(modTimeStr, 10, 64)
  153. if nil != err {
  154. logging.LogErrorf("parse mod time [%s] failed: %s", modTimeStr, err)
  155. c.Status(500)
  156. return
  157. }
  158. modTime := millisecond2Time(modTimeInt)
  159. if err = os.Chtimes(filePath, modTime, modTime); nil != err {
  160. logging.LogErrorf("change time failed: %s", err)
  161. ret.Code = -1
  162. ret.Msg = err.Error()
  163. return
  164. }
  165. }
  166. func millisecond2Time(t int64) time.Time {
  167. sec := t / 1000
  168. msec := t % 1000
  169. return time.Unix(sec, msec*int64(time.Millisecond))
  170. }