replace web/file by web/file2
This commit is contained in:
parent
b8acc5f8af
commit
6db99dd2bb
|
@ -3,8 +3,8 @@ package file
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file2/fs"
|
"dev.sum7.eu/genofire/golang-lib/web/file/fs"
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file2/s3"
|
"dev.sum7.eu/genofire/golang-lib/web/file/s3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fsType represents a type of file store.
|
// fsType represents a type of file store.
|
|
@ -1,8 +0,0 @@
|
||||||
package file
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
// Error Messages
|
|
||||||
var (
|
|
||||||
ErrUnsupportedStorageType = errors.New("storage type invalid")
|
|
||||||
)
|
|
|
@ -1,12 +0,0 @@
|
||||||
package file
|
|
||||||
|
|
||||||
import "github.com/google/uuid"
|
|
||||||
|
|
||||||
// File to store information in database
|
|
||||||
type File struct {
|
|
||||||
ID uuid.UUID `json:"id" gorm:"type:uuid;default:gen_random_uuid()" example:"32466d63-efa4-4f27-9f2b-a1f06c8e2e1d"`
|
|
||||||
StorageType string `json:"storage_type,omitempty"`
|
|
||||||
Path string `json:"path,omitempty"`
|
|
||||||
Filename string `json:"filename"`
|
|
||||||
ContentType string `json:"content-type"`
|
|
||||||
}
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file2"
|
"dev.sum7.eu/genofire/golang-lib/web/file"
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file2/fs"
|
"dev.sum7.eu/genofire/golang-lib/web/file/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOpenStat(t *testing.T) {
|
func TestOpenStat(t *testing.T) {
|
|
@ -1,63 +0,0 @@
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file"
|
|
||||||
)
|
|
||||||
|
|
||||||
// consts for filemanager
|
|
||||||
const (
|
|
||||||
StorageTypeFS = "fs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// error messages
|
|
||||||
var (
|
|
||||||
ErrPathNotExistsOrNoDirectory = errors.New("path invalid: not exists or not an directory")
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileManager to handle data on disk
|
|
||||||
type FileManager struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if filemanager could be used
|
|
||||||
func (m *FileManager) Check(s *file.Service) error {
|
|
||||||
info, err := os.Stat(s.Path)
|
|
||||||
if os.IsNotExist(err) || !info.IsDir() {
|
|
||||||
return ErrPathNotExistsOrNoDirectory
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save a file on disk and update file db
|
|
||||||
func (m *FileManager) Save(s *file.Service, file *file.File, src multipart.File) error {
|
|
||||||
file.ID = uuid.New()
|
|
||||||
file.Path = path.Join(file.ID.String(), file.Filename)
|
|
||||||
|
|
||||||
directory := path.Join(s.Path, file.ID.String())
|
|
||||||
os.Mkdir(directory, 0750)
|
|
||||||
|
|
||||||
out, err := os.Create(path.Join(s.Path, file.Path))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(out, src)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read get an reader of an file
|
|
||||||
func (m *FileManager) Read(s *file.Service, file *file.File) (io.Reader, error) {
|
|
||||||
return os.Open(path.Join(s.Path, file.Path))
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
file.AddManager(StorageTypeFS, &FileManager{})
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web"
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCheck(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
service := file.Service{
|
|
||||||
StorageType: StorageTypeFS,
|
|
||||||
Path: "./test",
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(service.Check())
|
|
||||||
|
|
||||||
service.StorageType = "s3"
|
|
||||||
assert.ErrorIs(file.ErrUnsupportedStorageType, service.Check())
|
|
||||||
|
|
||||||
service.StorageType = StorageTypeFS
|
|
||||||
service.Path = "./main_test.go"
|
|
||||||
assert.ErrorIs(ErrPathNotExistsOrNoDirectory, service.Check())
|
|
||||||
|
|
||||||
/* TODO no write permission
|
|
||||||
service.Path = "/dev"
|
|
||||||
assert.ErrorIs(ErrPathNotExistsOrNoDirectory, service.Check())
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSave(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
service := file.Service{
|
|
||||||
StorageType: "s3",
|
|
||||||
Path: "./test",
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := service.Upload(nil)
|
|
||||||
assert.ErrorIs(file.ErrUnsupportedStorageType, err)
|
|
||||||
|
|
||||||
service.StorageType = StorageTypeFS
|
|
||||||
req, err := web.NewRequestWithFile("localhost", "./test/00000000-0000-0000-0000-000000000000/a.txt")
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.NotNil(req)
|
|
||||||
|
|
||||||
_, err = service.Upload(req)
|
|
||||||
assert.NoError(err)
|
|
||||||
|
|
||||||
service.Path = "/dev"
|
|
||||||
_, err = service.Upload(req)
|
|
||||||
assert.True(os.IsNotExist(err))
|
|
||||||
//assert.True(os.IsPermission(err))
|
|
||||||
|
|
||||||
// TODO no write permission
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRead(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
service := file.Service{
|
|
||||||
StorageType: "s3",
|
|
||||||
Path: "./test",
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := service.Read(nil)
|
|
||||||
assert.ErrorIs(file.ErrUnsupportedStorageType, err)
|
|
||||||
|
|
||||||
service.StorageType = StorageTypeFS
|
|
||||||
|
|
||||||
file := &file.File{
|
|
||||||
Path: "00000000-0000-0000-0000-000000000000/a.txt",
|
|
||||||
}
|
|
||||||
r, err := service.Read(file)
|
|
||||||
assert.NoError(err)
|
|
||||||
buf := &strings.Builder{}
|
|
||||||
_, err = io.Copy(buf, r)
|
|
||||||
assert.Equal("Hello world\n", buf.String())
|
|
||||||
|
|
||||||
service.Path = "/dev"
|
|
||||||
_, err = service.Read(file)
|
|
||||||
assert.True(os.IsNotExist(err))
|
|
||||||
|
|
||||||
// TODO no write permission
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
Hello world
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file2"
|
"dev.sum7.eu/genofire/golang-lib/web/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestFS struct {
|
type TestFS struct {
|
|
@ -1,98 +0,0 @@
|
||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
const storageTypeDummy = "dummy"
|
|
||||||
|
|
||||||
type dummyManager struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *dummyManager) Check(s *Service) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *dummyManager) Save(s *Service, file *File, src multipart.File) error {
|
|
||||||
if src == nil {
|
|
||||||
return errors.New("nothing to fill")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *dummyManager) Read(s *Service, file *File) (io.Reader, error) {
|
|
||||||
b := bytes.Buffer{}
|
|
||||||
b.WriteString("Hello world\n")
|
|
||||||
return &b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
AddManager(storageTypeDummy, &dummyManager{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheck(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
service := Service{
|
|
||||||
StorageType: storageTypeDummy,
|
|
||||||
Path: "./fs/test",
|
|
||||||
}
|
|
||||||
assert.NoError(service.Check())
|
|
||||||
|
|
||||||
service.StorageType = "s3"
|
|
||||||
assert.ErrorIs(ErrUnsupportedStorageType, service.Check())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSave(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
service := Service{
|
|
||||||
StorageType: "fs",
|
|
||||||
Path: "./fs/test",
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := service.Upload(nil)
|
|
||||||
assert.ErrorIs(ErrUnsupportedStorageType, err)
|
|
||||||
|
|
||||||
service.StorageType = storageTypeDummy
|
|
||||||
_, err = service.GINUpload(&gin.Context{Request: &http.Request{}})
|
|
||||||
assert.ErrorIs(err, http.ErrNotMultipart)
|
|
||||||
|
|
||||||
req, err := web.NewRequestWithFile("http://localhost/upload", "./fs/test/00000000-0000-0000-0000-000000000000/a.txt")
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.NotNil(req)
|
|
||||||
|
|
||||||
_, err = service.Upload(req)
|
|
||||||
assert.NoError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRead(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
service := Service{
|
|
||||||
StorageType: "fs",
|
|
||||||
Path: "./fs/test",
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := service.Read(nil)
|
|
||||||
assert.ErrorIs(ErrUnsupportedStorageType, err)
|
|
||||||
|
|
||||||
service.StorageType = "dummy"
|
|
||||||
|
|
||||||
file := &File{
|
|
||||||
Path: "00000000-0000-0000-0000-000000000000/a.txt",
|
|
||||||
}
|
|
||||||
r, err := service.Read(file)
|
|
||||||
assert.NoError(err)
|
|
||||||
buf := &strings.Builder{}
|
|
||||||
_, err = io.Copy(buf, r)
|
|
||||||
assert.Equal("Hello world\n", buf.String())
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileManager interface {
|
|
||||||
Check(s *Service) error
|
|
||||||
Save(s *Service, fileObj *File, file multipart.File) error
|
|
||||||
Read(s *Service, fileObj *File) (io.Reader, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
managers = make(map[string]FileManager)
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddManager(typ string, m FileManager) {
|
|
||||||
managers[typ] = m
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package s3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error Messages during connect
|
|
||||||
var (
|
|
||||||
ErrNoPassword = errors.New("no secret access key found")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Connect try to use a path to setup a connection to s3 server
|
|
||||||
func Connect(path string) (*minio.Client, string, error) {
|
|
||||||
u, err := url.Parse(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
tls := u.Scheme[len(u.Scheme)-1] == 's'
|
|
||||||
accessKeyID := u.User.Username()
|
|
||||||
secretAccessKey, ok := u.User.Password()
|
|
||||||
if !ok {
|
|
||||||
return nil, "", ErrNoPassword
|
|
||||||
}
|
|
||||||
query := u.Query()
|
|
||||||
bucketName := query.Get("bucket")
|
|
||||||
location := query.Get("location")
|
|
||||||
|
|
||||||
u.User = nil
|
|
||||||
u.RawQuery = ""
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Initialize minio client object.
|
|
||||||
minioClient, err := minio.New(u.String(), &minio.Options{
|
|
||||||
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
|
|
||||||
Secure: tls,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create and check for bucket
|
|
||||||
err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location})
|
|
||||||
if err != nil {
|
|
||||||
if exists, err := minioClient.BucketExists(ctx, bucketName); err != nil || !exists {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return minioClient, bucketName, err
|
|
||||||
}
|
|
|
@ -5,7 +5,8 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/web/file2"
|
"dev.sum7.eu/genofire/golang-lib/web/file"
|
||||||
|
"dev.sum7.eu/genofire/golang-lib/web/file/s3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: actually test, either using little dummies or using sth like play.min.io
|
// TODO: actually test, either using little dummies or using sth like play.min.io
|
|
@ -1,69 +0,0 @@
|
||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Service to handle file-uploads in golang
|
|
||||||
type Service struct {
|
|
||||||
StorageType string `toml:"storage_type"`
|
|
||||||
Path string `toml:"path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if Service is configurated and useable
|
|
||||||
func (s *Service) Check() error {
|
|
||||||
mgmt, ok := managers[s.StorageType]
|
|
||||||
if !ok {
|
|
||||||
return ErrUnsupportedStorageType
|
|
||||||
}
|
|
||||||
return mgmt.Check(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload a file to storage
|
|
||||||
func (s *Service) Upload(request *http.Request) (*File, error) {
|
|
||||||
mgmt, ok := managers[s.StorageType]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrUnsupportedStorageType
|
|
||||||
}
|
|
||||||
file, fileRequest, err := request.FormFile("file")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fileObj := File{
|
|
||||||
Filename: filepath.Base(fileRequest.Filename),
|
|
||||||
}
|
|
||||||
|
|
||||||
// detect contenttype
|
|
||||||
buffer := make([]byte, 512)
|
|
||||||
n, err := file.Read(buffer)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fileObj.ContentType = http.DetectContentType(buffer[:n])
|
|
||||||
|
|
||||||
// Reset the read pointer
|
|
||||||
file.Seek(0, io.SeekStart)
|
|
||||||
if err := mgmt.Save(s, &fileObj, file); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &fileObj, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// GINUpload a file to storage using gin-gonic
|
|
||||||
func (s *Service) GINUpload(c *gin.Context) (*File, error) {
|
|
||||||
return s.Upload(c.Request)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a file to storage
|
|
||||||
func (s *Service) Read(file *File) (io.Reader, error) {
|
|
||||||
mgmt, ok := managers[s.StorageType]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrUnsupportedStorageType
|
|
||||||
}
|
|
||||||
return mgmt.Read(s, file)
|
|
||||||
}
|
|
Loading…
Reference in New Issue