@@ -30,8 +30,12 @@ import (
3030 "path"
3131 "path/filepath"
3232 "strings"
33+ "time"
3334
35+ "github.com/apache/answer/internal/entity"
36+ "github.com/apache/answer/internal/repo/file"
3437 "github.com/apache/answer/internal/service/file_record"
38+ "github.com/google/uuid"
3539
3640 "github.com/apache/answer/internal/base/constant"
3741 "github.com/apache/answer/internal/base/reason"
6569 }
6670)
6771
72+ var (
73+ FileStorageMode = os .Getenv ("FILE_STORAGE_MODE" ) // eg "fs" or "db"
74+ )
75+
6876type UploaderService interface {
6977 UploadAvatarFile (ctx * gin.Context , userID string ) (url string , err error )
7078 UploadPostFile (ctx * gin.Context , userID string ) (url string , err error )
@@ -78,13 +86,15 @@ type uploaderService struct {
7886 serviceConfig * service_config.ServiceConfig
7987 siteInfoService siteinfo_common.SiteInfoCommonService
8088 fileRecordService * file_record.FileRecordService
89+ fileRepo file.FileRepo
8190}
8291
8392// NewUploaderService new upload service
8493func NewUploaderService (
8594 serviceConfig * service_config.ServiceConfig ,
8695 siteInfoService siteinfo_common.SiteInfoCommonService ,
8796 fileRecordService * file_record.FileRecordService ,
97+ fileRepo file.FileRepo ,
8898) UploaderService {
8999 for _ , subPath := range subPathList {
90100 err := dir .CreateDirIfNotExist (filepath .Join (serviceConfig .UploadPath , subPath ))
@@ -96,9 +106,14 @@ func NewUploaderService(
96106 serviceConfig : serviceConfig ,
97107 siteInfoService : siteInfoService ,
98108 fileRecordService : fileRecordService ,
109+ fileRepo : fileRepo ,
99110 }
100111}
101112
113+ func UseDbStorage () bool {
114+ return FileStorageMode != "fs"
115+ }
116+
102117// UploadAvatarFile upload avatar file
103118func (us * uploaderService ) UploadAvatarFile (ctx * gin.Context , userID string ) (url string , err error ) {
104119 url , err = us .tryToUploadByPlugin (ctx , plugin .UserAvatar )
@@ -126,8 +141,8 @@ func (us *uploaderService) UploadAvatarFile(ctx *gin.Context, userID string) (ur
126141 }
127142
128143 newFilename := fmt .Sprintf ("%s%s" , uid .IDStr12 (), fileExt )
129- avatarFilePath := path . Join ( constant . AvatarSubPath , newFilename )
130- return us .uploadImageFile (ctx , fileHeader , avatarFilePath )
144+ fileHeader . Filename = newFilename
145+ return us .uploadImageFile (ctx , fileHeader , constant . AvatarSubPath )
131146}
132147
133148func (us * uploaderService ) AvatarThumbFile (ctx * gin.Context , fileName string , size int ) (url string , err error ) {
@@ -209,12 +224,14 @@ func (us *uploaderService) UploadPostFile(ctx *gin.Context, userID string) (
209224
210225 fileExt := strings .ToLower (path .Ext (fileHeader .Filename ))
211226 newFilename := fmt .Sprintf ("%s%s" , uid .IDStr12 (), fileExt )
212- avatarFilePath := path .Join (constant .PostSubPath , newFilename )
213- url , err = us .uploadImageFile (ctx , fileHeader , avatarFilePath )
227+ fileHeader .Filename = newFilename
228+ url , err = us .uploadImageFile (ctx , fileHeader , constant .PostSubPath )
229+ postFilePath := path .Join (constant .PostSubPath , newFilename )
230+
214231 if err != nil {
215232 return "" , err
216233 }
217- us .fileRecordService .AddFileRecord (ctx , userID , avatarFilePath , url , string (plugin .UserPost ))
234+ us .fileRecordService .AddFileRecord (ctx , userID , postFilePath , url , string (plugin .UserPost ))
218235 return url , nil
219236}
220237
@@ -279,10 +296,9 @@ func (us *uploaderService) UploadBrandingFile(ctx *gin.Context, userID string) (
279296 if _ , ok := plugin.DefaultFileTypeCheckMapping [plugin.AdminBranding ][fileExt ]; ! ok {
280297 return "" , errors .BadRequest (reason .RequestFormatError ).WithError (err )
281298 }
282-
283299 newFilename := fmt .Sprintf ("%s%s" , uid .IDStr12 (), fileExt )
284- avatarFilePath := path . Join ( constant . BrandingSubPath , newFilename )
285- return us .uploadImageFile (ctx , fileHeader , avatarFilePath )
300+ fileHeader . Filename = newFilename
301+ return us .uploadImageFile (ctx , fileHeader , constant . BrandingSubPath )
286302}
287303
288304func (us * uploaderService ) uploadImageFile (ctx * gin.Context , file * multipart.FileHeader , fileSubPath string ) (
@@ -295,7 +311,37 @@ func (us *uploaderService) uploadImageFile(ctx *gin.Context, file *multipart.Fil
295311 if err != nil {
296312 return "" , err
297313 }
298- filePath := path .Join (us .serviceConfig .UploadPath , fileSubPath )
314+ if UseDbStorage () {
315+ src , err := file .Open ()
316+ if err != nil {
317+ return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
318+ }
319+ defer src .Close ()
320+
321+ buffer := new (bytes.Buffer )
322+ if _ , err = io .Copy (buffer , src ); err != nil {
323+ return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
324+ }
325+
326+ file := & entity.File {
327+ ID : uuid .New ().String (),
328+ FileName : file .Filename ,
329+ MimeType : file .Header .Get ("Content-Type" ),
330+ Size : int64 (len (buffer .Bytes ())),
331+ Content : buffer .Bytes (),
332+ CreatedAt : time .Now (),
333+ }
334+
335+ err = us .fileRepo .Save (ctx , file )
336+ if err != nil {
337+ return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
338+ }
339+
340+ return fmt .Sprintf ("%s/answer/api/v1/file/%s/%s" , siteGeneral .SiteUrl , fileSubPath , file .ID ), nil
341+ //TODO checks: DecodeAndCheckImageFile removeExif
342+ }
343+ filePath := path .Join (us .serviceConfig .UploadPath , fileSubPath , file .Filename )
344+
299345 if err := ctx .SaveUploadedFile (file , filePath ); err != nil {
300346 return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
301347 }
@@ -324,6 +370,35 @@ func (us *uploaderService) uploadAttachmentFile(ctx *gin.Context, file *multipar
324370 if err != nil {
325371 return "" , err
326372 }
373+ if UseDbStorage () {
374+ src , err := file .Open ()
375+ if err != nil {
376+ return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
377+ }
378+ defer src .Close ()
379+
380+ buf := new (bytes.Buffer )
381+ if _ , err = io .Copy (buf , src ); err != nil {
382+ return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
383+ }
384+
385+ blob := & entity.File {
386+ ID : uuid .New ().String (),
387+ FileName : originalFilename ,
388+ MimeType : file .Header .Get ("Content-Type" ),
389+ Size : int64 (len (buf .Bytes ())),
390+ Content : buf .Bytes (),
391+ CreatedAt : time .Now (),
392+ }
393+
394+ err = us .fileRepo .Save (ctx , blob )
395+ if err != nil {
396+ return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
397+ }
398+
399+ downloadUrl = fmt .Sprintf ("%s/answer/api/v1/file/%s?download=%s" , siteGeneral .SiteUrl , blob .ID , url .QueryEscape (originalFilename ))
400+ return downloadUrl , nil
401+ }
327402 filePath := path .Join (us .serviceConfig .UploadPath , fileSubPath )
328403 if err := ctx .SaveUploadedFile (file , filePath ); err != nil {
329404 return "" , errors .InternalServer (reason .UnknownError ).WithError (err ).WithStack ()
0 commit comments