From edd3cc890f0a70d161bac12fa15029a717b909e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 23 May 2019 16:46:28 +0200 Subject: [PATCH] MM-15480: Add server support for Range on files (#10892) --- api4/file.go | 12 ++++++------ app/file.go | 2 +- services/filesstore/filesstore.go | 7 ++++++- services/filesstore/localstore.go | 2 +- services/filesstore/s3store.go | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/api4/file.go b/api4/file.go index 9ed8be9230..3426a04844 100644 --- a/api4/file.go +++ b/api4/file.go @@ -546,7 +546,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { } defer fileReader.Close() - err = writeFileResponse(info.Name, info.MimeType, info.Size, *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) + err = writeFileResponse(info.Name, info.MimeType, info.Size, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) if err != nil { c.Err = err return @@ -588,7 +588,7 @@ func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) { } defer fileReader.Close() - err = writeFileResponse(info.Name, THUMBNAIL_IMAGE_TYPE, 0, *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) + err = writeFileResponse(info.Name, THUMBNAIL_IMAGE_TYPE, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) if err != nil { c.Err = err return @@ -663,7 +663,7 @@ func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) { } defer fileReader.Close() - err = writeFileResponse(info.Name, PREVIEW_IMAGE_TYPE, 0, *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) + err = writeFileResponse(info.Name, PREVIEW_IMAGE_TYPE, 0, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, forceDownload, w, r) if err != nil { c.Err = err return @@ -729,14 +729,14 @@ func getPublicFile(c *Context, w http.ResponseWriter, r *http.Request) { } defer fileReader.Close() - err = writeFileResponse(info.Name, info.MimeType, info.Size, *c.App.Config().ServiceSettings.WebserverMode, fileReader, false, w, r) + err = writeFileResponse(info.Name, info.MimeType, info.Size, time.Unix(0, info.UpdateAt*int64(1000*1000)), *c.App.Config().ServiceSettings.WebserverMode, fileReader, false, w, r) if err != nil { c.Err = err return } } -func writeFileResponse(filename string, contentType string, contentSize int64, webserverMode string, fileReader io.Reader, forceDownload bool, w http.ResponseWriter, r *http.Request) *model.AppError { +func writeFileResponse(filename string, contentType string, contentSize int64, lastModification time.Time, webserverMode string, fileReader io.ReadSeeker, forceDownload bool, w http.ResponseWriter, r *http.Request) *model.AppError { w.Header().Set("Cache-Control", "private, no-cache") w.Header().Set("X-Content-Type-Options", "nosniff") @@ -790,7 +790,7 @@ func writeFileResponse(filename string, contentType string, contentSize int64, w w.Header().Set("X-Frame-Options", "DENY") w.Header().Set("Content-Security-Policy", "Frame-ancestors 'none'") - io.Copy(w, fileReader) + http.ServeContent(w, r, filename, lastModification, fileReader) return nil } diff --git a/app/file.go b/app/file.go index 75f7b30909..764d117a42 100644 --- a/app/file.go +++ b/app/file.go @@ -81,7 +81,7 @@ func (a *App) ReadFile(path string) ([]byte, *model.AppError) { } // Caller must close the first return value -func (a *App) FileReader(path string) (io.ReadCloser, *model.AppError) { +func (a *App) FileReader(path string) (filesstore.ReadCloseSeeker, *model.AppError) { backend, err := a.FileBackend() if err != nil { return nil, err diff --git a/services/filesstore/filesstore.go b/services/filesstore/filesstore.go index 762998ad20..27f5bc9618 100644 --- a/services/filesstore/filesstore.go +++ b/services/filesstore/filesstore.go @@ -10,10 +10,15 @@ import ( "github.com/mattermost/mattermost-server/model" ) +type ReadCloseSeeker interface { + io.ReadCloser + io.Seeker +} + type FileBackend interface { TestConnection() *model.AppError - Reader(path string) (io.ReadCloser, *model.AppError) + Reader(path string) (ReadCloseSeeker, *model.AppError) ReadFile(path string) ([]byte, *model.AppError) FileExists(path string) (bool, *model.AppError) CopyFile(oldPath, newPath string) *model.AppError diff --git a/services/filesstore/localstore.go b/services/filesstore/localstore.go index 6678bb7ae5..3ad7744521 100644 --- a/services/filesstore/localstore.go +++ b/services/filesstore/localstore.go @@ -34,7 +34,7 @@ func (b *LocalFileBackend) TestConnection() *model.AppError { return nil } -func (b *LocalFileBackend) Reader(path string) (io.ReadCloser, *model.AppError) { +func (b *LocalFileBackend) Reader(path string) (ReadCloseSeeker, *model.AppError) { f, err := os.Open(filepath.Join(b.directory, path)) if err != nil { return nil, model.NewAppError("Reader", "api.file.reader.reading_local.app_error", nil, err.Error(), http.StatusInternalServerError) diff --git a/services/filesstore/s3store.go b/services/filesstore/s3store.go index 5b76be390c..517aa9d9b1 100644 --- a/services/filesstore/s3store.go +++ b/services/filesstore/s3store.go @@ -84,7 +84,7 @@ func (b *S3FileBackend) TestConnection() *model.AppError { } // Caller must close the first return value -func (b *S3FileBackend) Reader(path string) (io.ReadCloser, *model.AppError) { +func (b *S3FileBackend) Reader(path string) (ReadCloseSeeker, *model.AppError) { s3Clnt, err := b.s3New() if err != nil { return nil, model.NewAppError("Reader", "api.file.reader.s3.app_error", nil, err.Error(), http.StatusInternalServerError)