Add file upload mechanism

This commit is contained in:
Sonja Happ 2019-05-17 16:27:59 +02:00
parent 4dfa4fd5f4
commit 75f8ef280d
5 changed files with 177 additions and 40 deletions

View file

@ -10,12 +10,14 @@ import (
func ProvideErrorResponse(c *gin.Context, err error) bool {
if err != nil {
if err == gorm.ErrRecordNotFound {
errormsg := "Record not Found in DB: " + err.Error()
c.JSON(http.StatusNotFound, gin.H{
"error": "No files found in DB",
"error": errormsg,
})
} else {
errormsg := "Error on DB Query or transaction: " + err.Error()
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Error on DB Query or transaction",
"error": errormsg,
})
}
return true // Error

View file

@ -4,16 +4,12 @@ import (
"strconv"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/json"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func FilesRegister(r *gin.RouterGroup) {
r.GET("/", filesReadEp)
r.POST("/", fileRegistrationEp) // NEW in API
r.PUT("/:FileID", fileUpdateEp) // NEW in API
r.GET("/:FileID", fileReadEp)
r.DELETE("/:FileID", fileDeleteEp)
@ -32,28 +28,7 @@ func filesReadEp(c *gin.Context) {
}
func fileRegistrationEp(c *gin.Context) {
var m map[string]interface{}
decoder := json.NewDecoder(c.Request.Body)
defer c.Request.Body.Close()
if err := decoder.Decode(&m); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Bad request. Invalid body.",
})
return;
}
// Database query
err := AddFile(m)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}
func fileUpdateEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{

View file

@ -37,17 +37,4 @@ func FindFile(userID int, fileID string) ( common.File, error) {
return file, err
}
func AddFile(m map[string]interface{}) error {
// TODO deserialize m (JSON file object) to data struct File
// TODO we need the user here as well to be able to create the association in the DB
// TODO add deserialized File to DB
var err error
return err
}

View file

@ -2,7 +2,12 @@ package simulation
import (
"github.com/gin-gonic/gin"
"fmt"
"net/http"
"path/filepath"
"strconv"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func SimulationsRegister(r *gin.RouterGroup) {
@ -11,6 +16,7 @@ func SimulationsRegister(r *gin.RouterGroup) {
r.PUT("/:SimulationID", simulationUpdateEp)
r.GET("/:SimulationID", simulationReadEp)
r.DELETE("/:SimulationID", simulationDeleteEp)
r.POST ("/:SimulationID/models/:SimulationModelID/file", fileRegistrationEp) // NEW
}
func simulationsReadEp(c *gin.Context) {
@ -45,3 +51,66 @@ func simulationDeleteEp(c *gin.Context) {
})
}
func fileRegistrationEp(c *gin.Context) {
// TODO Check if file upload is ok for this user or simulation (user or simulation exists)
var widgetID_s = c.Param("WidgetID")
var widgetID_i int
var simulationmodelID_s = c.Param("SimulationModelID")
var simulationmodelID_i int
if widgetID_s != "" {
widgetID_i, _ = strconv.Atoi(widgetID_s)
} else {
widgetID_i = -1
}
if simulationmodelID_s != "" {
simulationmodelID_i, _ = strconv.Atoi(simulationmodelID_s)
} else {
simulationmodelID_i = -1
}
if simulationmodelID_i == -1 && widgetID_i == -1 {
errormsg := fmt.Sprintf("Bad request. Did not provide simulation model ID or widget ID for file")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return;
}
// Extract file from POST request form
file, 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.Get("Content-Type") // TODO make sure this is properly set in file header
filename := filepath.Base(file.Filename)
foldername := "files/testfolder" //TODO replace this placeholder with systematic foldername (e.g. simulation ID)
size := file.Size
// Save file to local disc (NOT DB!)
err = SaveFile(file, filename, foldername, uint(size))
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
err = AddFile(filename, foldername, filetype, uint(size), widgetID_i, simulationmodelID_i )
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}

View file

@ -1,6 +1,11 @@
package simulation
import (
"io"
"mime/multipart"
"os"
"path/filepath"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
@ -17,3 +22,102 @@ func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) {
err := db.Model(user).Related(&simulations, "Simulations").Error
return simulations, len(simulations), err
}
func AddFile(filename string, foldername string, filetype string, size uint, widgetID int, simulationmodelID int ) error {
filesavepath := filepath.Join(foldername, filename)
// get last modify time of target file
fileinfo, err := os.Stat(filesavepath)
if err != nil {
return err
}
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 {
// associate to Widget
var widget common.Widget
err := db.First(&widget, widgetID).Error
if err != nil {
return err
}
err = db.Model(&widget).Association("Files").Append(&fileObj).Error
}
if simulationmodelID != -1 {
// associate to Simulation Model
var model common.SimulationModel
err := db.First(&model, simulationmodelID).Error
if err != nil {
return err
}
err = db.Model(&model).Association("Files").Append(&fileObj).Error
}
return err
}
func SaveFile(file *multipart.FileHeader, filename string, foldername string, size uint, ) error {
filesavepath := filepath.Join(foldername, filename)
// Ensure folder with name foldername exists
err := os.MkdirAll(foldername, os.ModePerm)
if err != nil {
// TODO error handling
return err
}
fileTarget , errcreate := os.OpenFile(filesavepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
if errcreate != nil {
// TODO error handling: File could not be created
return errcreate
}
defer fileTarget.Close()
// Save file to target path
uploadedFile, erropen := file.Open()
if erropen != nil {
// TODO error handling
return erropen
}
defer uploadedFile.Close()
var uploadContent = make([]byte, size)
for {
n, err := uploadedFile.Read(uploadContent)
if err != nil && err != io.EOF {
// TODO error handling
return err
}
if n == 0 {
break
}
_, err = fileTarget.Write(uploadContent[:n])
if err != nil {
// TODO error handling
return err
}
}
return err
}