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

395 lines
No EOL
9.7 KiB
Go

package file
import (
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strconv"
"github.com/gin-gonic/gin"
_ "github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
)
//func FindAllFiles() ([]common.File, int, error) {
// db := common.GetDB()
// var files []common.File
// err := db.Find(&files).Error
// if err != nil {
// // print error message to screen
// fmt.Println(fmt.Errorf("DB Error in FindAllFiles(): %q", err).Error())
// }
// return files, len(files), err
//}
//
//func FindUserFiles(user *common.User) ([]common.File, int, error) {
// db := common.GetDB()
// var files []common.File
// err := db.Model(user).Related(&files, "Files").Error
// return files, len(files), err
//}
//
//func FindFile(userID int, fileID string) ( common.File, error) {
// var file common.File
// db := common.GetDB()
// fileID_i, _ := strconv.Atoi(fileID)
//
// err := db.First(&file, fileID_i).Error
//
// return file, err
//
//}
func FindFiles(c *gin.Context, widgetID int, modelID int, simulationID int) ([]common.File, int, error){
db := common.GetDB()
var files []common.File
var err error
if widgetID != -1 {
var w common.Widget
err = db.First(&w, widgetID).Error
if err != nil {
return files, 0, err
}
err = db.Model(&w).Related(&files, "Files").Error
if err != nil {
return files, 0, err
}
} else if modelID != -1 {
var m common.Model
err = db.First(&m, modelID).Error
if err != nil {
return files, 0, err
}
err = db.Model(&m).Related(&files, "Files").Error
if err != nil {
return files, 0, err
}
}
return files, len(files), err
}
func FindFileByPath(path string) (common.File, error) {
var file common.File
db := common.GetDB()
err := db.Where("Path = ?", path).Find(file).Error
return file, err
}
func RegisterFile(c *gin.Context, widgetID int, modelID int, simulationID int){
// Extract file from PUT request form
file_header, err := c.FormFile("file")
if err != nil {
errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return;
}
// Obtain properties of file
filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header
filename := filepath.Base(file_header.Filename)
foldername := getFolderName(simulationID, modelID, widgetID)
size := file_header.Size
// Check if simulation and widget or model exist in DB
_, err = simulation.FindSimulation(simulationID)
if common.ProvideErrorResponse(c, err) {
return
}
if modelID != -1 {
_, err = model.FindModel(modelID)
if common.ProvideErrorResponse(c, err) {
return
}
} else if widgetID != -1 {
_, err = widget.FindWidget(widgetID)
if common.ProvideErrorResponse(c, err) {
return
}
}
// Save file to local disc (NOT DB!)
err = modifyFileOnDisc(file_header, filename, foldername, uint(size), true)
if err != nil {
errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg,
})
return
}
// Add File object with parameters to DB
saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, true)
}
func UpdateFile(c *gin.Context, widgetID int, modelID int, simulationID int){
// Extract file from PUT request form
err := c.Request.ParseForm()
if err != nil {
errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return;
}
file_header, err := c.FormFile("file")
if err != nil {
errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return;
}
filename := filepath.Base(file_header.Filename)
filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header
size := file_header.Size
foldername := getFolderName(simulationID, modelID, widgetID)
err = modifyFileOnDisc(file_header, filename, foldername, uint(size), false)
if err != nil {
errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg,
})
return
}
saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, false)
}
func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){
//contentType := c.GetHeader("Content-Type")
//TODO currently returns first file it finds in DB
db := common.GetDB()
var fileInDB common.File
if widgetID != -1 {
// get associated Widget
var wdgt common.Widget
err := db.First(&wdgt, modelID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&wdgt).Related(&fileInDB).Error
if common.ProvideErrorResponse(c, err) {
return
}
} else if modelID != -1 {
// get associated Simulation Model
var model common.Model
err := db.First(&model, modelID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&model).Related(&fileInDB).Error
if common.ProvideErrorResponse(c, err) {
return
}
}
//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-Transfer-Encoding", "binary")
c.Header("Content-Disposition", "attachment; filename="+fileInDB.Name )
//c.Header("Content-Type", contentType)
c.File(fileInDB.Path)
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
func DeleteFile(c *gin.Context, widgetID int, nmodelID int, simulationID int){
// TODO
}
func saveFileInDB(c *gin.Context, filename string, foldername string, filetype string, size uint, widgetID int, modelID int, createObj bool) {
filesavepath := filepath.Join(foldername, filename)
// get last modify time of target file
fileinfo, err := os.Stat(filesavepath)
if err != nil {
errormsg := fmt.Sprintf("Internal Server Error. Error stat on file: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg,
})
return
}
modTime := fileinfo.ModTime()
// Create file object for Database
var fileObj common.File
fileObj.Size = uint(size)
fileObj.Type = filetype
fileObj.Path = filesavepath
fileObj.Date = modTime
fileObj.Name = filename
fileObj.ImageHeight = 0
fileObj.ImageWidth = 0
// Check if file shall be associated with Widget or Simulation Model
db := common.GetDB()
if widgetID != -1 {
if createObj {
// associate to Widget
var wdgt common.Widget
err := db.First(&wdgt, widgetID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&wdgt).Association("Files").Append(&fileObj).Error
} else {
// update file obj in DB
fileInDB, err := FindFileByPath(filesavepath)
if common.ProvideErrorResponse(c, err){
return
}
err = db.Model(&fileInDB).Where("Path = ?", filesavepath).Updates(map[string]interface{}{"Size": fileObj.Size, "Date": fileObj.Date, "ImageHeight": fileObj.ImageHeight, "ImageWidth": fileObj.ImageWidth}).Error
}
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
"fileID": fileObj.ID,
})
return
}
}
if modelID != -1 {
if createObj {
// associate to Simulation Model
db := common.GetDB()
var model common.Model
err := db.First(&model, modelID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&model).Association("Files").Append(&fileObj).Error
} else {
// update file obj in DB
fileInDB, err := FindFileByPath(filesavepath)
if common.ProvideErrorResponse(c, err){
return
}
err = db.Model(&fileInDB).Where("Path = ?", filesavepath).Updates(map[string]interface{}{"Size": fileObj.Size, "Date": fileObj.Date, "ImageHeight": fileObj.ImageHeight, "ImageWidth": fileObj.ImageWidth}).Error
}
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
"fileID": fileObj.ID,
})
return
}
}
}
func modifyFileOnDisc(file_header *multipart.FileHeader, filename string, foldername string, size uint, createFile bool) error {
filesavepath := filepath.Join(foldername, filename)
var err error
if createFile {
// Ensure folder with name foldername exists
err = os.MkdirAll(foldername, os.ModePerm)
} else {
// test if file exists
_, err = os.Stat(filesavepath)
}
if err != nil {
return err
}
var open_options int
if createFile {
// create file it not exists, file MUST not exist
open_options = os.O_RDWR|os.O_CREATE|os.O_EXCL
} else {
open_options = os.O_RDWR
}
fileTarget , err := os.OpenFile(filesavepath, open_options, 0666)
if err != nil {
return err
}
defer fileTarget.Close()
// Save file to target path
uploadedFile, err := file_header.Open()
if err != nil {
return err
}
defer uploadedFile.Close()
var uploadContent = make([]byte, size)
for {
n, err := uploadedFile.Read(uploadContent)
if err != nil && err != io.EOF {
return err
}
if n == 0 {
break
}
_, err = fileTarget.Write(uploadContent[:n])
if err != nil {
return err
}
}
return err
}
func getFolderName(simulationID int, modelID int, widgetID int) string {
base_foldername := "files/"
elementname := ""
elementid := 0
if modelID == -1{
elementname = "/widget_"
elementid = widgetID
} else {
elementname = "/model_"
elementid = modelID
}
foldername := base_foldername + "simulation_"+ strconv.Itoa(simulationID) + elementname + strconv.Itoa(elementid) + "/"
return foldername
}