VILLASweb-backend-go/routes/file/file_methods.go

255 lines
6.4 KiB
Go

/**
* This file is part of VILLASweb-backend-go
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
package file
import (
"fmt"
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"path/filepath"
"strings"
"time"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
)
type File struct {
database.File
}
func (f *File) ByID(id uint) error {
db := database.GetDB()
err := db.Find(f, id).Error
if err != nil {
return err
}
return nil
}
func (f *File) save() error {
db := database.GetDB()
err := db.Create(f).Error
return err
}
func (f *File) download(c *gin.Context) error {
if f.Key == "" {
// Seems this headers needed for some browsers (for example without this headers Chrome will download files as txt)
c.Header("Content-Description", "File Transfer")
c.Header("Content-Disposition", "attachment; filename="+f.Name)
c.Header("Expires", "")
c.Header("Cache-Control", "")
c.Data(http.StatusOK, f.Type, f.FileData)
} else {
url, err := f.getS3Url()
if err != nil {
return fmt.Errorf("failed to presign S3 request: %s", err)
}
c.Redirect(http.StatusFound, url)
}
return nil
}
func (f *File) Register(fileHeader *multipart.FileHeader, scenarioID uint) error {
// Obtain properties of file
f.Type = fileHeader.Header.Get("Content-Type")
f.Name = filepath.Base(fileHeader.Filename)
f.Size = uint(fileHeader.Size)
f.Date = time.Now().String()
f.ScenarioID = scenarioID
// set file data
fileContent, err := fileHeader.Open()
if err != nil {
return err
}
defer fileContent.Close()
bucket, err := configuration.GlobalConfig.String("s3.bucket")
if err != nil || bucket == "" {
// s3 object storage not used, s3.bucket param is empty
// save file to postgres DB
f.FileData, err = ioutil.ReadAll(fileContent)
if err != nil {
return err
}
f.Key = ""
} else {
err := f.putS3(fileContent)
if err != nil {
return fmt.Errorf("failed to upload to S3 bucket: %s", err)
}
log.Println("Saved new file in S3 object storage")
}
// Add image dimensions in case the file is an image
if strings.Contains(f.Type, "image") || strings.Contains(f.Type, "Image") {
// set the file reader back to the start of the file
_, err := fileContent.Seek(0, 0)
if err == nil {
imageConfig, _, err := image.DecodeConfig(fileContent)
if err != nil {
log.Println("unable to decode image configuration: Dimensions of image file are not set, using default size 512x512, error:", err)
f.ImageWidth = 512
f.ImageHeight = 512
} else {
f.ImageHeight = imageConfig.Height
f.ImageWidth = imageConfig.Width
}
} else {
return fmt.Errorf("error on setting file reader back to start of file, dimensions not updated: %v", err)
}
}
// Add File object with parameters to DB
err = f.save()
if err != nil {
return err
}
// Create association to scenario
db := database.GetDB()
var so database.Scenario
err = db.Find(&so, scenarioID).Error
if err != nil {
return err
}
err = db.Model(&so).Association("Files").Append(f).Error
return err
}
func (f *File) update(fileHeader *multipart.FileHeader) error {
// set file data
fileContent, err := fileHeader.Open()
if err != nil {
return err
}
defer fileContent.Close()
bucket, err := configuration.GlobalConfig.String("s3.bucket")
if err != nil || bucket == "" {
// s3 object storage not used, s3.bucket param is empty
// save file to postgres DB
f.FileData, err = ioutil.ReadAll(fileContent)
if err != nil {
return err
}
f.Key = ""
} else {
err := f.putS3(fileContent)
if err != nil {
return fmt.Errorf("failed to upload to S3 bucket: %s", err)
}
log.Println("Updated file in S3 object storage")
}
f.Type = fileHeader.Header.Get("Content-Type")
f.Size = uint(fileHeader.Size)
f.Date = time.Now().String()
f.Name = filepath.Base(fileHeader.Filename)
// Update image dimensions in case the file is an image
if strings.Contains(f.Type, "image") || strings.Contains(f.Type, "Image") {
// set the file reader back to the start of the file
_, err := fileContent.Seek(0, 0)
if err == nil {
imageConfig, _, err := image.DecodeConfig(fileContent)
if err != nil {
log.Println("Unable to decode image configuration: Dimensions of image file are not updated.", err)
}
f.ImageHeight = imageConfig.Height
f.ImageWidth = imageConfig.Width
} else {
log.Println("Error on setting file reader back to start of file, dimensions not updated::", err)
}
} else {
f.ImageWidth = 0
f.ImageHeight = 0
}
// Add File object with parameters to DB
db := database.GetDB()
err = db.Model(f).Updates(map[string]interface{}{
"Size": f.Size,
"FileData": f.FileData,
"Date": f.Date,
"Name": f.Name,
"Type": f.Type,
"ImageHeight": f.ImageHeight,
"ImageWidth": f.ImageWidth,
"Key": f.Key,
}).Error
return err
}
func (f *File) Delete() error {
db := database.GetDB()
// remove association between file and scenario
var so database.Scenario
err := db.Find(&so, f.ScenarioID).Error
if err != nil {
return err
}
// delete file from s3 bucket
if f.Key != "" {
// TODO we do not delete the file from s3 object storage
// to ensure that no data is lost if multiple File objects reference the same S3 data object
// This behavior should be replaced by a different file handling in the future
//err = f.deleteS3()
//if err != nil {
// return err
//}
//log.Println("Deleted file in S3 object storage")
log.Printf("Did NOT delete file with Key %v in S3 object storage!\n", f.Key)
}
err = db.Model(&so).Association("Files").Delete(f).Error
if err != nil {
return err
}
// delete file from DB
err = db.Delete(f).Error
return err
}