diff --git a/src/github.com/turt2live/matrix-media-repo/controllers/upload_controller/upload_controller.go b/src/github.com/turt2live/matrix-media-repo/controllers/upload_controller/upload_controller.go index f2ef97cd8acd80ac7a0249ed7d1d1975a8be2587..ff2a3afd73623b2a6c7e1374514a0bc70ce99399 100644 --- a/src/github.com/turt2live/matrix-media-repo/controllers/upload_controller/upload_controller.go +++ b/src/github.com/turt2live/matrix-media-repo/controllers/upload_controller/upload_controller.go @@ -196,20 +196,26 @@ func StoreDirect(contents io.ReadCloser, contentType string, filename string, us return nil, err } - // TODO: Re-enable restore from backup - //// If the media's file exists, we'll delete the temp file - //// If the media's file doesn't exist, we'll move the temp file to where the media expects it to be - //targetPath, err2 := storage.ResolveMediaLocation(ctx, log, media.DatastoreId, media.Location) - //if err2 != nil { - // return nil, err2 - //} - //exists, err := util.FileExists(targetPath) - //if err != nil || !exists { - // // We'll assume an error means it doesn't exist - // os.Rename(fileLocation, targetPath) - //} else { - // ds.DeleteObject(info.Location) - //} + // If the media's file exists, we'll delete the temp file + // If the media's file doesn't exist, we'll move the temp file to where the media expects it to be + if media.DatastoreId != ds.DatastoreId && media.Location != info.Location { + ds2, err := datastore.LocateDatastore(ctx, log, media.DatastoreId) + if err != nil { + ds.DeleteObject(info.Location) // delete temp object + return nil, err + } + if !ds2.ObjectExists(media.Location) { + stream, err := ds.DownloadFile(info.Location) + if err != nil { + return nil, err + } + + ds2.OverwriteObject(media.Location, stream, ctx, log) + ds.DeleteObject(info.Location) + } else { + ds.DeleteObject(info.Location) + } + } trackUploadAsLastAccess(ctx, log, media) return media, nil diff --git a/src/github.com/turt2live/matrix-media-repo/storage/datastore/datastore_ref.go b/src/github.com/turt2live/matrix-media-repo/storage/datastore/datastore_ref.go index 72b2aad7b4ed5d17da434667358815ad110c60a4..f518edeffdfc56d914aaefc71f3e52284a6f6803 100644 --- a/src/github.com/turt2live/matrix-media-repo/storage/datastore/datastore_ref.go +++ b/src/github.com/turt2live/matrix-media-repo/storage/datastore/datastore_ref.go @@ -12,6 +12,7 @@ import ( "github.com/turt2live/matrix-media-repo/storage/datastore/ds_file" "github.com/turt2live/matrix-media-repo/storage/datastore/ds_s3" "github.com/turt2live/matrix-media-repo/types" + "github.com/turt2live/matrix-media-repo/util" ) type DatastoreRef struct { @@ -70,10 +71,43 @@ func (d *DatastoreRef) DownloadFile(location string) (io.ReadCloser, error) { } else if d.Type == "s3" { s3, err := ds_s3.GetOrCreateS3Datastore(d.DatastoreId, d.config) if err != nil { - return nil,err + return nil, err } return s3.DownloadObject(location) } else { return nil, errors.New("unknown datastore type") } -} \ No newline at end of file +} + +func (d *DatastoreRef) ObjectExists(location string) bool { + if d.Type == "file" { + ok, err := util.FileExists(path.Join(d.Uri, location)) + if err != nil { + return false + } + return ok + } else if d.Type == "s3" { + s3, err := ds_s3.GetOrCreateS3Datastore(d.DatastoreId, d.config) + if err != nil { + return false + } + return s3.ObjectExists(location) + } else { + panic("unknown datastore type") + } +} + +func (d *DatastoreRef) OverwriteObject(location string, stream io.ReadCloser, ctx context.Context, log *logrus.Entry) error { + if d.Type == "file" { + _, _, err := ds_file.PersistFileAtLocation(path.Join(d.Uri, location), stream, ctx, log) + return err + } else if d.Type == "s3" { + s3, err := ds_s3.GetOrCreateS3Datastore(d.DatastoreId, d.config) + if err != nil { + return err + } + return s3.OverwriteObject(location, stream) + } else { + return errors.New("unknown datastore type") + } +} diff --git a/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_file/file_store.go b/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_file/file_store.go index bb7ca4ef854f532a2bb6d5584da2115abf0bf312..cf727bdbc5553b44462c5ff9294fadc1255c2b55 100644 --- a/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_file/file_store.go +++ b/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_file/file_store.go @@ -53,10 +53,24 @@ func PersistFile(basePath string, file io.ReadCloser, ctx context.Context, log * return nil, err } - f, err := os.OpenFile(targetFile, os.O_WRONLY|os.O_CREATE, 0644) + sizeBytes, hash, err := PersistFileAtLocation(targetFile, file, ctx, log) if err != nil { return nil, err } + + locationPath := path.Join(primaryContainer, secondaryContainer, fileName) + return &types.ObjectInfo{ + Location: locationPath, + Sha256Hash: hash, + SizeBytes: sizeBytes, + }, nil +} + +func PersistFileAtLocation(targetFile string, file io.ReadCloser, ctx context.Context, log *logrus.Entry) (int64, string, error) { + f, err := os.OpenFile(targetFile, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return 0, "", err + } defer f.Close() rfile, wfile := io.Pipe() @@ -91,19 +105,14 @@ func PersistFile(basePath string, file io.ReadCloser, ctx context.Context, log * if hashErr != nil { defer os.Remove(targetFile) - return nil, hashErr + return 0, "", hashErr } if writeErr != nil { - return nil, writeErr + return 0, "", writeErr } - locationPath := path.Join(primaryContainer, secondaryContainer, fileName) - return &types.ObjectInfo{ - Location: locationPath, - Sha256Hash: hash, - SizeBytes: sizeBytes, - }, nil + return sizeBytes, hash, nil } func DeletePersistedFile(basePath string, location string) error { diff --git a/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_s3/s3_store.go b/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_s3/s3_store.go index f02ad753b44ce3927c8f476112bf5e2e40e2c569..19130570f78f3cbef47ba2aaa03884477f3edf5c 100644 --- a/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_s3/s3_store.go +++ b/src/github.com/turt2live/matrix-media-repo/storage/datastore/ds_s3/s3_store.go @@ -131,3 +131,16 @@ func (s *s3Datastore) DownloadObject(location string) (io.ReadCloser, error) { logrus.Info("Downloading object from bucket ", s.bucket, ": ", location) return s.client.GetObject(s.bucket, location, minio.GetObjectOptions{}) } + +func (s *s3Datastore) ObjectExists(location string) bool { + stat, err := s.client.StatObject(s.bucket, location, minio.StatObjectOptions{}) + if err != nil { + return false + } + return stat.Size > 0 +} + +func (s *s3Datastore) OverwriteObject(location string, stream io.ReadCloser) error { + _, err := s.client.PutObject(s.bucket, location, stream, -1, minio.PutObjectOptions{}) + return err +}