mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
Add file upload mechanism
This commit is contained in:
parent
4dfa4fd5f4
commit
75f8ef280d
5 changed files with 177 additions and 40 deletions
|
@ -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
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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.",
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue