Commit 661f2049 authored by Steffen Vogel's avatar Steffen Vogel 🎅🏼
Browse files

added support for storing files in S3 object storage

parent ebc1ae16
......@@ -38,59 +38,83 @@ func InitConfig() error {
}
var (
dbHost = flag.String("db-host", "/var/run/postgresql", "Host of the PostgreSQL database (default is /var/run/postgresql for localhost DB on Ubuntu systems)")
dbName = flag.String("db-name", "villasdb", "Name of the database to use (default is villasdb)")
dbUser = flag.String("db-user", "", "Username of database connection (default is <empty>)")
dbPass = flag.String("db-pass", "", "Password of database connection (default is <empty>)")
dbSSLMode = flag.String("db-ssl-mode", "disable", "SSL mode of DB (default is disable)") // TODO: change default for production
amqpHost = flag.String("amqp-host", "", "If set, use this as host for AMQP broker (default is disabled)")
amqpUser = flag.String("amqp-user", "", "Username for AMQP broker")
amqpPass = flag.String("amqp-pass", "", "Password for AMQP broker")
configFile = flag.String("config", "", "Path to YAML configuration file")
mode = flag.String("mode", "release", "Select debug/release/test mode (default is release)")
port = flag.String("port", "4000", "Port of the backend (default is 4000)")
baseHost = flag.String("base-host", "localhost:4000", "The host at which the backend is hosted (default: localhost)")
basePath = flag.String("base-path", "/api/v2", "The path at which the API routes are located (default /api/v2)")
adminUser = flag.String("admin-user", "", "Initial admin username")
adminPass = flag.String("admin-pass", "", "Initial admin password")
adminMail = flag.String("admin-mail", "", "Initial admin mail address")
dbHost = flag.String("db-host", "/var/run/postgresql", "Host of the PostgreSQL database (default is /var/run/postgresql for localhost DB on Ubuntu systems)")
dbName = flag.String("db-name", "villasdb", "Name of the database to use (default is villasdb)")
dbUser = flag.String("db-user", "", "Username of database connection (default is <empty>)")
dbPass = flag.String("db-pass", "", "Password of database connection (default is <empty>)")
dbSSLMode = flag.String("db-ssl-mode", "disable", "SSL mode of DB (default is disable)") // TODO: change default for production
amqpHost = flag.String("amqp-host", "", "If set, use this as host for AMQP broker (default is disabled)")
amqpUser = flag.String("amqp-user", "", "Username for AMQP broker")
amqpPass = flag.String("amqp-pass", "", "Password for AMQP broker")
configFile = flag.String("config", "", "Path to YAML configuration file")
mode = flag.String("mode", "release", "Select debug/release/test mode (default is release)")
port = flag.String("port", "4000", "Port of the backend (default is 4000)")
baseHost = flag.String("base-host", "localhost:4000", "The host at which the backend is hosted (default: localhost)")
basePath = flag.String("base-path", "/api/v2", "The path at which the API routes are located (default /api/v2)")
adminUser = flag.String("admin-user", "", "Initial admin username")
adminPass = flag.String("admin-pass", "", "Initial admin password")
adminMail = flag.String("admin-mail", "", "Initial admin mail address")
s3Bucket = flag.String("s3-bucket", "", "S3 Bucket for uploading files")
s3Endpoint = flag.String("s3-endpoint", "", "Endpoint of S3 API for file uploads")
s3Region = flag.String("s3-region", "us-east-1", "S3 Region for file uploads")
s3NoSSL = flag.Bool("s3-nossl", false, "Use encrypted connections to the S3 API")
s3PathStyle = flag.Bool("s3-pathstyle", false, "Use path-style S3 API")
)
flag.Parse()
static := map[string]string{
"db.host": *dbHost,
"db.name": *dbName,
"db.user": *dbUser,
"db.pass": *dbPass,
"db.ssl": *dbSSLMode,
"amqp.host": *amqpHost,
"amqp.user": *amqpUser,
"amqp.pass": *amqpPass,
"mode": *mode,
"port": *port,
"base.host": *baseHost,
"base.path": *basePath,
"admin.user": *adminUser,
"admin.pass": *adminPass,
"admin.mail": *adminMail,
"db.host": *dbHost,
"db.name": *dbName,
"db.user": *dbUser,
"db.pass": *dbPass,
"db.ssl": *dbSSLMode,
"amqp.host": *amqpHost,
"amqp.user": *amqpUser,
"amqp.pass": *amqpPass,
"mode": *mode,
"port": *port,
"base.host": *baseHost,
"base.path": *basePath,
"admin.user": *adminUser,
"admin.pass": *adminPass,
"admin.mail": *adminMail,
"s3.bucket": *s3Bucket,
"s3.endpoint": *s3Endpoint,
"s3.region": *s3Region,
}
if *s3NoSSL {
static["s3.nossl"] = "true"
} else {
static["s3.nossl"] = "false"
}
if *s3PathStyle {
static["s3.pathstyle"] = "true"
} else {
static["s3.pathstyle"] = "false"
}
mappings := map[string]string{
"DB_HOST": "db.host",
"DB_NAME": "db.name",
"DB_USER": "db.user",
"DB_PASS": "db.pass",
"DB_SSLMODE": "db.ssl",
"AMQP_HOST": "amqp.host",
"AMQP_USER": "amqp.user",
"AMQP_PASS": "amqp.pass",
"BASE_HOST": "base.host",
"BASE_PATH": "base.path",
"MODE": "mode",
"PORT": "port",
"ADMIN_USER": "admin.user",
"ADMIN_PASS": "admin.pass",
"ADMIN_MAIL": "admin.mail",
"DB_HOST": "db.host",
"DB_NAME": "db.name",
"DB_USER": "db.user",
"DB_PASS": "db.pass",
"DB_SSLMODE": "db.ssl",
"AMQP_HOST": "amqp.host",
"AMQP_USER": "amqp.user",
"AMQP_PASS": "amqp.pass",
"BASE_HOST": "base.host",
"BASE_PATH": "base.path",
"MODE": "mode",
"PORT": "port",
"ADMIN_USER": "admin.user",
"ADMIN_PASS": "admin.pass",
"ADMIN_MAIL": "admin.mail",
"S3_BUCKET": "s3.bucket",
"S3_ENDPOINT": "s3.endpoint",
"S3_REGION": "s3.region",
"S3_NOSSL": "s3.nossl",
}
defaults := config.NewStatic(static)
......
......@@ -22,9 +22,10 @@
package database
import (
"github.com/lib/pq"
"time"
"github.com/lib/pq"
"github.com/jinzhu/gorm/dialects/postgres"
)
......@@ -197,6 +198,8 @@ type File struct {
Model
// Name of file
Name string `json:"name" gorm:"not null"`
// Key of file in S3 bucket
Key string `json:"key"`
// Type of file (MIME type)
Type string `json:"type"`
// Size of file (in byte)
......
......@@ -2,6 +2,7 @@ module git.rwth-aachen.de/acs/public/villas/web-backend-go
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/aws/aws-sdk-go v1.35.35
github.com/chenjiandongx/ginprom v0.0.0-20191022035802-6f3da3c84986
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.4.0
......@@ -19,7 +20,7 @@ require (
github.com/swaggo/gin-swagger v1.2.0
github.com/swaggo/swag v1.6.3
github.com/zpatrick/go-config v0.0.0-20191104215613-50bc2709703f
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
gopkg.in/go-playground/validator.v9 v9.30.0
gopkg.in/ini.v1 v1.51.0 // indirect
)
......
......@@ -18,6 +18,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aws/aws-sdk-go v1.35.35 h1:o/EbgEcIPWga7GWhJhb3tiaxqk4/goTdo5YEMdnVxgE=
github.com/aws/aws-sdk-go v1.35.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
......@@ -105,6 +107,9 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
......@@ -151,6 +156,7 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
......@@ -209,6 +215,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
......@@ -228,6 +236,8 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
......@@ -248,10 +258,13 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
......@@ -288,6 +301,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
......@@ -22,8 +22,7 @@
package file
import (
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
"github.com/gin-gonic/gin"
"fmt"
"image"
_ "image/gif"
_ "image/jpeg"
......@@ -33,10 +32,13 @@ import (
"mime/multipart"
"net/http"
"path/filepath"
"strconv"
"strings"
"time"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
)
......@@ -62,20 +64,25 @@ func (f *File) save() error {
func (f *File) download(c *gin.Context) error {
// create unique file name
filename := "file_" + strconv.FormatUint(uint64(f.ID), 10) + "_" + f.Name
// detect the content type of the file
contentType := http.DetectContentType(f.FileData)
//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="+filename)
c.Data(http.StatusOK, contentType, f.FileData)
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)
......@@ -85,12 +92,21 @@ func (f *File) register(fileHeader *multipart.FileHeader, scenarioID uint) error
// set file data
fileContent, err := fileHeader.Open()
defer fileContent.Close()
if err != nil {
return err
}
f.FileData, err = ioutil.ReadAll(fileContent)
defer fileContent.Close()
bucket, err := configuration.GolbalConfig.String("s3.bucket")
if bucket == "" {
f.FileData, err = ioutil.ReadAll(fileContent)
f.Key = ""
} else {
err := f.putS3(fileContent)
if err != nil {
return fmt.Errorf("Failed to upload to S3 bucket: %s", err)
}
}
// Add image dimensions in case the file is an image
if strings.Contains(f.Type, "image") || strings.Contains(f.Type, "Image") {
......@@ -100,13 +116,13 @@ func (f *File) register(fileHeader *multipart.FileHeader, scenarioID uint) error
imageConfig, _, err := image.DecodeConfig(fileContent)
if err != nil {
log.Println("Unable to decode image configuration: Dimensions of image file are not set: ", err)
} else {
f.ImageHeight = imageConfig.Height
f.ImageWidth = imageConfig.Width
return fmt.Errorf("Unable to decode image configuration: Dimensions of image file are not set: ", 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)
return fmt.Errorf("Error on setting file reader back to start of file, dimensions not updated: ", err)
}
}
......@@ -134,49 +150,54 @@ func (f *File) update(fileHeader *multipart.FileHeader) error {
// set file data
fileContent, err := fileHeader.Open()
defer fileContent.Close()
if err != nil {
return err
}
fileData, err := ioutil.ReadAll(fileContent)
defer fileContent.Close()
bucket, err := configuration.GolbalConfig.String("s3.bucket")
if bucket == "" {
f.FileData, err = ioutil.ReadAll(fileContent)
f.Key = ""
} else {
err := f.putS3(fileContent)
if err != nil {
return fmt.Errorf("Failed to upload to S3 bucket: %s", err)
}
}
fileType := fileHeader.Header.Get("Content-Type")
imageHeight := f.ImageHeight
imageWidth := f.ImageWidth
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(fileType, "image") || strings.Contains(fileType, "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)
} else {
imageHeight = imageConfig.Height
imageWidth = imageConfig.Width
}
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 {
imageWidth = 0
imageHeight = 0
f.ImageWidth = 0
f.ImageHeight = 0
}
db := database.GetDB()
err = db.Model(f).Updates(map[string]interface{}{
"Size": uint(fileHeader.Size),
"FileData": fileData,
"Date": time.Now().String(),
"Name": filepath.Base(fileHeader.Filename),
"Type": fileType,
"ImageHeight": imageHeight,
"ImageWidth": imageWidth,
}).Error
// Add File object with parameters to DB
err = f.save()
if err != nil {
return err
}
return err
return nil
}
func (f *File) delete() error {
......@@ -189,6 +210,12 @@ func (f *File) delete() error {
if err != nil {
return err
}
// delete file from s3 bucket
if f.Key != "" {
f.deleteS3()
}
err = db.Model(&so).Association("Files").Delete(f).Error
if err != nil {
return err
......
/** File package, S3 uploads.
*
* @author Steffen Vogel <svogel2@eonerc.rwth-aachen.de>
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* 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"
"io"
"time"
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/google/uuid"
)
// Global session
var s3Session *session.Session = nil
func getS3Session() (*session.Session, error) {
if s3Session == nil {
var err error
s3Session, err = createS3Session()
if err != nil {
return nil, err
}
}
return s3Session, nil
}
func createS3Session() (*session.Session, error) {
endpoint, err := configuration.GolbalConfig.String("s3.endpoint")
region, err := configuration.GolbalConfig.String("s3.region")
pathStyle, err := configuration.GolbalConfig.Bool("s3.pathstyle")
nossl, err := configuration.GolbalConfig.Bool("s3.nossl")
sess, err := session.NewSession(
&aws.Config{
Region: aws.String(region),
Endpoint: aws.String(endpoint),
DisableSSL: aws.Bool(nossl),
S3ForcePathStyle: aws.Bool(pathStyle),
},
)
if err != nil {
return nil, err
}
return sess, nil
}
func (f *File) putS3(fileContent io.Reader) error {
bucket, err := configuration.GolbalConfig.String("s3.bucket")
if err != nil || bucket == "" {
return fmt.Errorf("No S3 bucket configured")
}
// The session the S3 Uploader will use
sess, err := getS3Session()
if err != nil {
return fmt.Errorf("Failed to create session: %s", err)
}
// Create an uploader with the session and default options
uploader := s3manager.NewUploader(sess)
f.Key = uuid.New().String()
f.FileData = nil
// Upload the file to S3.
_, err = uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(bucket),
Key: aws.String(f.Key),
Body: fileContent,
})
if err != nil {
return fmt.Errorf("Failed to upload file, %v", err)
}
return nil
}
func (f *File) getS3Url() (string, error) {
bucket, err := configuration.GolbalConfig.String("s3.bucket")
if err != nil || bucket == "" {
return "", fmt.Errorf("No S3 bucket configured")
}
// The session the S3 Uploader will use
sess, err := getS3Session()
if err != nil {
return "", fmt.Errorf("Failed to create session: %s", err)
}
// Create S3 service client
svc := s3.New(sess)
req, _ := svc.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(f.Key),
ResponseContentType: aws.String(f.Type),
ResponseContentDisposition: aws.String("attachment; filename=" + f.Name),
// ResponseContentEncoding: aws.String(),
// ResponseContentLanguage: aws.String(),
// ResponseCacheControl: aws.String(),
// ResponseExpires: aws.String(),
})
urlStr, err := req.Presign(5 * 24 * 60 * time.Minute)
if err != nil {
return "", err
}
return urlStr, nil
}
func (f *File) deleteS3() error {
bucket, err := configuration.GolbalConfig.String("s3.bucket")
if err != nil || bucket == "" {
return fmt.Errorf("No S3 bucket configured")
}
// The session the S3 Uploader will use
sess, err := getS3Session()
if err != nil {
return fmt.Errorf("Failed to create session: %s", err)
}
// Create S3 service client
svc := s3.New(sess)
_, err = svc.DeleteObject(&s3.DeleteObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(f.Key),
})
if err != nil {
return err
}
f.Key = ""
return nil
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment