upload.go 5.4 KB

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