2017-12-29 22:35:57 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-05-12 22:05:06 +00:00
|
|
|
"compress/gzip"
|
2024-03-18 12:42:19 +00:00
|
|
|
"crypto/sha256"
|
2017-12-29 22:35:57 +00:00
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"io"
|
|
|
|
"mime"
|
|
|
|
"path"
|
|
|
|
"strings"
|
2018-10-11 18:15:12 +00:00
|
|
|
"time"
|
2017-12-29 22:35:57 +00:00
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
2024-03-18 12:42:19 +00:00
|
|
|
"github.com/cheggaaa/pb/v3"
|
2021-10-19 21:28:40 +00:00
|
|
|
"github.com/gofrs/uuid"
|
|
|
|
"github.com/gosimple/slug"
|
2019-05-12 22:05:06 +00:00
|
|
|
"github.com/pkg/errors"
|
2017-12-29 22:35:57 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2024-03-18 12:42:19 +00:00
|
|
|
const barUpdateInterval = 100 * time.Millisecond
|
|
|
|
|
|
|
|
//revive:disable-next-line:flag-parameter // Fine in this case
|
2019-05-12 22:05:06 +00:00
|
|
|
func executeUpload(inFileName string, inFileHandle io.ReadSeeker, useCalculatedFilename bool, overrideMimeType string, forceGzip bool) (string, error) {
|
2017-12-29 22:35:57 +00:00
|
|
|
var (
|
|
|
|
upFile = inFileName
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if useCalculatedFilename {
|
|
|
|
if upFile, err = calculateUploadFilename(inFileName, inFileHandle); err != nil {
|
2021-08-25 23:32:20 +00:00
|
|
|
return "", errors.Wrap(err, "calculating upload filename")
|
2017-12-29 22:35:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mimeType := mime.TypeByExtension(path.Ext(upFile))
|
|
|
|
if mimeType == "" {
|
|
|
|
mimeType = "application/octet-stream"
|
|
|
|
}
|
|
|
|
|
2018-09-19 09:55:25 +00:00
|
|
|
if overrideMimeType != "" {
|
|
|
|
mimeType = overrideMimeType
|
|
|
|
}
|
|
|
|
|
2017-12-29 22:35:57 +00:00
|
|
|
log.Debugf("Uploading file to %q with type %q", upFile, mimeType)
|
|
|
|
|
2019-05-12 22:05:06 +00:00
|
|
|
var contentEncoding *string
|
|
|
|
if forceGzip {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
gw := gzip.NewWriter(buf)
|
|
|
|
|
|
|
|
if _, err := io.Copy(gw, inFileHandle); err != nil {
|
2021-08-25 23:32:20 +00:00
|
|
|
return "", errors.Wrap(err, "compressing file")
|
2019-05-12 22:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := gw.Close(); err != nil {
|
2021-08-25 23:32:20 +00:00
|
|
|
return "", errors.Wrap(err, "closing gzip writer")
|
2019-05-12 22:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inFileHandle = bytes.NewReader(buf.Bytes())
|
|
|
|
contentEncoding = aws.String("gzip")
|
|
|
|
}
|
|
|
|
|
2023-06-05 16:04:36 +00:00
|
|
|
var awsCfgs []*aws.Config
|
|
|
|
if cfg.Endpoint != "" {
|
|
|
|
awsCfgs = append(awsCfgs, &aws.Config{Endpoint: &cfg.Endpoint, S3ForcePathStyle: aws.Bool(true)})
|
|
|
|
}
|
|
|
|
|
|
|
|
sess := session.Must(session.NewSession(awsCfgs...))
|
2017-12-29 22:35:57 +00:00
|
|
|
svc := s3.New(sess)
|
|
|
|
|
2018-10-11 18:15:12 +00:00
|
|
|
ps, err := newProgressSeeker(inFileHandle)
|
|
|
|
if err != nil {
|
2017-12-29 22:35:57 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2018-10-11 18:15:12 +00:00
|
|
|
if cfg.Progress {
|
2024-03-18 12:42:19 +00:00
|
|
|
bar := pb.New64(ps.Size)
|
|
|
|
bar.Set(pb.Bytes, true)
|
|
|
|
bar.Set("prefix", inFileName)
|
2018-10-11 18:15:12 +00:00
|
|
|
bar.Start()
|
|
|
|
barUpdate := true
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for barUpdate {
|
2024-03-18 12:42:19 +00:00
|
|
|
bar.SetCurrent(ps.Progress)
|
|
|
|
time.Sleep(barUpdateInterval)
|
2018-10-11 18:15:12 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
barUpdate = false
|
2024-03-18 19:18:22 +00:00
|
|
|
bar.SetCurrent(ps.Progress)
|
2018-10-11 18:15:12 +00:00
|
|
|
bar.Finish()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2024-03-18 12:42:19 +00:00
|
|
|
if _, err = svc.PutObject(&s3.PutObjectInput{
|
2019-05-12 22:05:06 +00:00
|
|
|
Body: ps,
|
|
|
|
Bucket: aws.String(cfg.Bucket),
|
|
|
|
ContentEncoding: contentEncoding,
|
|
|
|
ContentType: aws.String(mimeType),
|
|
|
|
Key: aws.String(upFile),
|
2017-12-29 22:35:57 +00:00
|
|
|
}); err != nil {
|
2024-03-18 12:42:19 +00:00
|
|
|
return "", fmt.Errorf("putting object to S3: %w", err)
|
2017-12-29 22:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s%s", cfg.BaseURL, upFile), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func calculateUploadFilename(inFile string, inFileHandle io.ReadSeeker) (string, error) {
|
|
|
|
fileHash, err := hashFile(inFileHandle)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-10-19 21:28:40 +00:00
|
|
|
safeFileName := strings.Join([]string{
|
|
|
|
slug.Make(strings.TrimSuffix(path.Base(inFile), path.Ext(inFile))),
|
|
|
|
path.Ext(inFile),
|
|
|
|
}, "")
|
|
|
|
|
|
|
|
return executeTemplate(cfg.FileTemplate, map[string]interface{}{
|
|
|
|
"Ext": path.Ext(inFile),
|
|
|
|
"FileName": path.Base(inFile),
|
|
|
|
"Hash": fileHash,
|
|
|
|
"SafeFileName": safeFileName,
|
|
|
|
"UUID": uuid.Must(uuid.NewV4()).String(),
|
2017-12-29 22:35:57 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-03-18 12:42:19 +00:00
|
|
|
func hashFile(inFileHandle io.ReadSeeker) (hexHash string, err error) {
|
|
|
|
if _, err = inFileHandle.Seek(0, io.SeekStart); err != nil {
|
|
|
|
return "", fmt.Errorf("resetting reader: %w", err)
|
2017-12-29 22:35:57 +00:00
|
|
|
}
|
|
|
|
|
2024-03-18 12:42:19 +00:00
|
|
|
shaHash := sha256.New()
|
|
|
|
if _, err = io.Copy(shaHash, inFileHandle); err != nil {
|
|
|
|
return "", fmt.Errorf("reading data into hash: %w", err)
|
2017-12-29 22:35:57 +00:00
|
|
|
}
|
2024-03-18 12:42:19 +00:00
|
|
|
|
|
|
|
return fmt.Sprintf("%x", shaHash.Sum(nil)), nil
|
2017-12-29 22:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func executeTemplate(tplStr string, vars map[string]interface{}) (string, error) {
|
2021-10-19 21:28:40 +00:00
|
|
|
tpl, err := template.New("filename").Parse(tplStr)
|
2017-12-29 22:35:57 +00:00
|
|
|
if err != nil {
|
2024-03-18 12:42:19 +00:00
|
|
|
return "", fmt.Errorf("parsing filename template: %w", err)
|
2017-12-29 22:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buf := new(bytes.Buffer)
|
2024-03-18 12:42:19 +00:00
|
|
|
if err = tpl.Execute(buf, vars); err != nil {
|
|
|
|
return "", fmt.Errorf("executing filename template: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String(), nil
|
2017-12-29 22:35:57 +00:00
|
|
|
}
|