upload.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // SiYuan - Refactor your thinking
  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 model
  17. import (
  18. "errors"
  19. "io"
  20. "os"
  21. "path"
  22. "path/filepath"
  23. "strings"
  24. "github.com/88250/gulu"
  25. "github.com/88250/lute/ast"
  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/sql"
  30. "github.com/siyuan-note/siyuan/kernel/treenode"
  31. "github.com/siyuan-note/siyuan/kernel/util"
  32. )
  33. func InsertLocalAssets(id string, assetPaths []string, isUpload bool) (succMap map[string]interface{}, err error) {
  34. succMap = map[string]interface{}{}
  35. bt := treenode.GetBlockTree(id)
  36. if nil == bt {
  37. err = errors.New(Conf.Language(71))
  38. return
  39. }
  40. docDirLocalPath := filepath.Join(util.DataDir, bt.BoxID, path.Dir(bt.Path))
  41. assetsDirPath := getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), docDirLocalPath)
  42. if !gulu.File.IsExist(assetsDirPath) {
  43. if err = os.MkdirAll(assetsDirPath, 0755); nil != err {
  44. return
  45. }
  46. }
  47. for _, p := range assetPaths {
  48. fName := filepath.Base(p)
  49. fName = util.FilterUploadFileName(fName)
  50. ext := filepath.Ext(fName)
  51. fName = strings.TrimSuffix(fName, ext)
  52. ext = strings.ToLower(ext)
  53. fName += ext
  54. baseName := fName
  55. if gulu.File.IsDir(p) || !isUpload {
  56. if !strings.HasPrefix(p, "\\\\") {
  57. p = "file://" + p
  58. }
  59. succMap[baseName] = p
  60. continue
  61. }
  62. fi, statErr := os.Stat(p)
  63. if nil != statErr {
  64. err = statErr
  65. return
  66. }
  67. f, openErr := os.Open(p)
  68. if nil != openErr {
  69. err = openErr
  70. return
  71. }
  72. hash, hashErr := util.GetEtagByHandle(f, fi.Size())
  73. if nil != hashErr {
  74. f.Close()
  75. return
  76. }
  77. if existAsset := sql.QueryAssetByHash(hash); nil != existAsset {
  78. // 已经存在同样数据的资源文件的话不重复保存
  79. succMap[baseName] = existAsset.Path
  80. } else {
  81. ext := path.Ext(fName)
  82. fName = fName[0 : len(fName)-len(ext)]
  83. fName = fName + "-" + ast.NewNodeID() + ext
  84. writePath := filepath.Join(assetsDirPath, fName)
  85. if _, err = f.Seek(0, io.SeekStart); nil != err {
  86. f.Close()
  87. return
  88. }
  89. if err = filelock.WriteFileByReader(writePath, f); nil != err {
  90. f.Close()
  91. return
  92. }
  93. f.Close()
  94. succMap[baseName] = "assets/" + fName
  95. }
  96. }
  97. IncSync()
  98. return
  99. }
  100. func Upload(c *gin.Context) {
  101. ret := gulu.Ret.NewResult()
  102. defer c.JSON(200, ret)
  103. form, err := c.MultipartForm()
  104. if nil != err {
  105. logging.LogErrorf("insert asset failed: %s", err)
  106. ret.Code = -1
  107. ret.Msg = err.Error()
  108. return
  109. }
  110. assetsDirPath := filepath.Join(util.DataDir, "assets")
  111. if nil != form.Value["id"] {
  112. id := form.Value["id"][0]
  113. bt := treenode.GetBlockTree(id)
  114. if nil == bt {
  115. ret.Code = -1
  116. ret.Msg = Conf.Language(71)
  117. return
  118. }
  119. docDirLocalPath := filepath.Join(util.DataDir, bt.BoxID, path.Dir(bt.Path))
  120. assetsDirPath = getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), docDirLocalPath)
  121. }
  122. relAssetsDirPath := "assets"
  123. if nil != form.Value["assetsDirPath"] {
  124. relAssetsDirPath = form.Value["assetsDirPath"][0]
  125. assetsDirPath = filepath.Join(util.DataDir, relAssetsDirPath)
  126. }
  127. if !gulu.File.IsExist(assetsDirPath) {
  128. if err = os.MkdirAll(assetsDirPath, 0755); nil != err {
  129. ret.Code = -1
  130. ret.Msg = err.Error()
  131. return
  132. }
  133. }
  134. var errFiles []string
  135. succMap := map[string]interface{}{}
  136. files := form.File["file[]"]
  137. for _, file := range files {
  138. fName := file.Filename
  139. fName = util.FilterUploadFileName(fName)
  140. ext := filepath.Ext(fName)
  141. fName = strings.TrimSuffix(fName, ext)
  142. ext = strings.ToLower(ext)
  143. fName += ext
  144. baseName := fName
  145. f, openErr := file.Open()
  146. if nil != openErr {
  147. errFiles = append(errFiles, fName)
  148. ret.Msg = openErr.Error()
  149. break
  150. }
  151. hash, hashErr := util.GetEtagByHandle(f, file.Size)
  152. if nil != hashErr {
  153. errFiles = append(errFiles, fName)
  154. ret.Msg = err.Error()
  155. f.Close()
  156. break
  157. }
  158. if existAsset := sql.QueryAssetByHash(hash); nil != existAsset {
  159. // 已经存在同样数据的资源文件的话不重复保存
  160. succMap[baseName] = existAsset.Path
  161. } else {
  162. fName = util.AssetName(fName)
  163. writePath := filepath.Join(assetsDirPath, fName)
  164. if _, err = f.Seek(0, io.SeekStart); nil != err {
  165. errFiles = append(errFiles, fName)
  166. ret.Msg = err.Error()
  167. f.Close()
  168. break
  169. }
  170. if err = filelock.WriteFileByReader(writePath, f); nil != err {
  171. errFiles = append(errFiles, fName)
  172. ret.Msg = err.Error()
  173. f.Close()
  174. break
  175. }
  176. f.Close()
  177. succMap[baseName] = strings.TrimPrefix(path.Join(relAssetsDirPath, fName), "/")
  178. }
  179. }
  180. ret.Data = map[string]interface{}{
  181. "errFiles": errFiles,
  182. "succMap": succMap,
  183. }
  184. IncSync()
  185. }
  186. func getAssetsDir(boxLocalPath, docDirLocalPath string) (assets string) {
  187. assets = filepath.Join(docDirLocalPath, "assets")
  188. if !gulu.File.IsExist(assets) {
  189. assets = filepath.Join(boxLocalPath, "assets")
  190. if !gulu.File.IsExist(assets) {
  191. assets = filepath.Join(util.DataDir, "assets")
  192. }
  193. }
  194. return
  195. }