-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ADD] metadata & request struct for file handling
- Loading branch information
1 parent
25d0667
commit d46d0cc
Showing
7 changed files
with
234 additions
and
55 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package app | ||
|
||
import ( | ||
"flag" | ||
"log" | ||
"net/http" | ||
"os" | ||
"time" | ||
) | ||
|
||
var ( | ||
APP_PORT = os.Getenv("APP_PORT") | ||
) | ||
|
||
func App() { | ||
// parse addr flag to dynamicaly change address value | ||
addr := flag.String("addr", APP_PORT, "HTTP network address") | ||
|
||
// parse debug mode to enable debug mode in application | ||
debug := flag.Bool("debug", false, "Enable debug mode") | ||
// define logs | ||
infoLog := log.New(os.Stderr, "INFO:\t", log.Ldate|log.Ltime) | ||
errorLog := log.New(os.Stderr, "ERROR:\t", log.Ldate|log.Ltime|log.Llongfile) | ||
|
||
flag.Parse() | ||
|
||
if *debug { | ||
infoLog.Println("Enabled debug mode...") | ||
} | ||
|
||
_ = &Application{ | ||
Debug: debug, | ||
InfoLog: infoLog, | ||
ErrorLog: errorLog, | ||
} | ||
|
||
srv := http.Server{ | ||
Addr: *addr, | ||
ErrorLog: errorLog, | ||
IdleTimeout: time.Minute, | ||
ReadTimeout: 5 * time.Second, | ||
WriteTimeout: 10 * time.Second, | ||
} | ||
|
||
err := srv.ListenAndServe() | ||
if err != nil { | ||
errorLog.Fatalln(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package app | ||
|
||
import ( | ||
"github.com/Nicolas-ggd/filestream" | ||
"log" | ||
) | ||
|
||
// Application struct is a wrap of application, which controls everything and have top management role, everything is united around this struct. | ||
type Application struct { | ||
Debug *bool | ||
ErrorLog *log.Logger | ||
InfoLog *log.Logger | ||
RFileRequest *fstream.RFileRequest | ||
File *fstream.File | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,7 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"github.com/Nicolas-ggd/filestream/cmd" | ||
"log" | ||
"net/http" | ||
"os" | ||
"time" | ||
) | ||
|
||
var ( | ||
APP_PORT = os.Getenv("APP_PORT") | ||
) | ||
import "github.com/Nicolas-ggd/filestream/cmd/app" | ||
|
||
func main() { | ||
// parse addr flag to dynamicaly change address value | ||
addr := flag.String("addr", APP_PORT, "HTTP network address") | ||
|
||
// parse debug mode to enable debug mode in application | ||
debug := flag.Bool("debug", false, "Enable debug mode") | ||
// define logs | ||
infoLog := log.New(os.Stderr, "INFO:\t", log.Ldate|log.Ltime) | ||
errorLog := log.New(os.Stderr, "ERROR:\t", log.Ldate|log.Ltime|log.Llongfile) | ||
|
||
flag.Parse() | ||
|
||
if *debug { | ||
infoLog.Println("Enabled debug mode...") | ||
} | ||
|
||
_ = &cmd.Application{ | ||
Debug: debug, | ||
InfoLog: infoLog, | ||
ErrorLog: errorLog, | ||
} | ||
|
||
srv := http.Server{ | ||
Addr: *addr, | ||
ErrorLog: errorLog, | ||
IdleTimeout: time.Minute, | ||
ReadTimeout: 5 * time.Second, | ||
WriteTimeout: 10 * time.Second, | ||
} | ||
|
||
err := srv.ListenAndServe() | ||
if err != nil { | ||
errorLog.Fatalln(err) | ||
} | ||
app.App() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package fstream | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"math" | ||
"mime/multipart" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/google/uuid" | ||
) | ||
|
||
// File struct is final face of uploaded file, it includes necessary field to use them after file is uploaded | ||
type File struct { | ||
// Original uploaded file name | ||
FileName string | ||
// FileUniqueName is unique name | ||
FileUniqueName string | ||
// Uploaded file path | ||
FilePath string | ||
// Uploaded file extension | ||
FileExtension string | ||
// Uploaded file size | ||
FileSize string | ||
} | ||
|
||
// RFileRequest struct is used for http request, use this struct to bind uploaded file | ||
type RFileRequest struct { | ||
// File is an interface to access the file part of a multipart message. | ||
File multipart.File | ||
// A FileHeader describes a file part of a multipart request. | ||
UploadFile *multipart.FileHeader | ||
// Maximum range of chunk uploads | ||
MaxRange int | ||
// Uploaded file size | ||
FileSize int | ||
// Upload directory | ||
UploadDirectory string | ||
// FileUniqueName is identifier to generate unique name for files | ||
FileUniqueName bool | ||
} | ||
|
||
// generateUuidUniqueName function generates unique string using UUID | ||
func (r *File) generateUuidUniqueName(request *RFileRequest) { | ||
ext := filepath.Ext(request.UploadFile.Filename) | ||
|
||
id, err := uuid.NewUUID() | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
r.FileUniqueName = fmt.Sprintf("%s%s", id.String(), ext) | ||
} | ||
|
||
// RemoveUploadedFile function removes uploaded file from uploaded directory, it takes param and returns nothing: | ||
// | ||
// RFileRequest struct | ||
func RemoveUploadedFile(r *RFileRequest) { | ||
filePath := filepath.Join(r.UploadDirectory, r.UploadFile.Filename) | ||
|
||
e := os.Remove(filePath) | ||
if e != nil { | ||
log.Printf("error removing file: %v", e) | ||
} | ||
} | ||
|
||
// prettyByteSize function is used to concrete the file size | ||
func prettyByteSize(b int) string { | ||
bf := float64(b) | ||
|
||
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { | ||
if math.Abs(bf) < 1024.0 { | ||
return fmt.Sprintf("%3.1f%sB", bf, unit) | ||
} | ||
bf /= 1024.0 | ||
} | ||
|
||
return fmt.Sprintf("%.1fYiB", bf) | ||
} | ||
|
||
// StoreChunk cares slice of chunks and returns final results and error | ||
// | ||
// File - struct is final version about file information | ||
// | ||
// error - functions cares about errors and returns error | ||
// | ||
// function creates new directory for chunks if it doesn't exist, if directory already exists it appends received chunks in current chunks and if entire file is uploaded then File struct is returned | ||
func StoreChunk(r *RFileRequest) (*File, error) { | ||
var rFile *File | ||
|
||
// Create new directory for uploaded chunk | ||
filePath := filepath.Join(r.UploadDirectory + r.UploadFile.Filename) | ||
|
||
// Create new directory if it doesn't exist | ||
if _, err := os.Stat(r.UploadDirectory); os.IsNotExist(err) { | ||
err = os.MkdirAll(r.UploadDirectory, 0777) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create new temporary directory: %v", err) | ||
} | ||
} | ||
|
||
// Open the file for appending and creating if it doesn't exist | ||
f, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) | ||
if err != nil { | ||
return nil, fmt.Errorf("error opening file: %v", err) | ||
} | ||
defer func() { | ||
if cerr := f.Close(); cerr != nil { | ||
log.Printf("error closing file: %v", cerr) | ||
} | ||
}() | ||
|
||
// Copy the chunk data to the file | ||
if _, err := io.Copy(f, r.File); err != nil { | ||
return nil, fmt.Errorf("failed to copying file: %v", err) | ||
} | ||
|
||
// If the entire file is uploaded, finalize entire process and return file information | ||
if r.MaxRange >= r.FileSize { | ||
fileInfo, err := f.Stat() | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to stating file: %v", err) | ||
} | ||
|
||
// Calculate file size in bytes | ||
size := prettyByteSize(int(fileInfo.Size())) | ||
|
||
// Bind File struct and return | ||
rFile = &File{ | ||
FileName: r.UploadFile.Filename, | ||
FileUniqueName: r.UploadFile.Filename, | ||
FilePath: filePath, | ||
FileExtension: filepath.Ext(r.UploadFile.Filename), | ||
FileSize: size, | ||
} | ||
} | ||
|
||
return rFile, nil | ||
} | ||
|
||
// IsAllowExtension function checks if file extension is allowed to upload, it takes following params | ||
// | ||
// fileExtensions - array of strings, which is looks like: []string{".jpg", ".jpeg"}, note that this is fileExtensions which is allowed to receive | ||
// | ||
// fileName - string, this parameter is file name which is like ".jpeg", ".jpg" | ||
// | ||
// Returns: | ||
// | ||
// bool - function returns false if extension isn't allowed to receive, it returns true if extension is allowed to receive | ||
func IsAllowExtension(fileExtensions []string, fileName string) bool { | ||
ext := strings.ToLower(filepath.Ext(fileName)) | ||
|
||
// range over the received extensions to check if file is ok to accept | ||
for _, allowed := range fileExtensions { | ||
if ext == allowed { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
module github.com/Nicolas-ggd/filestream | ||
|
||
go 1.23.0 | ||
|
||
require github.com/google/uuid v1.6.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |