diff --git a/README.md b/README.md index 8ec2ef9..bd21d6f 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,75 @@ Here’s how you can get involved: ``` make test ``` -4. To start application run: - ``` - make start - ``` + +## Usage +```go +import( + "github.com/gin-gonic/gin" + "github.com/Nicolas-ggd/filestream" +) + +func Upload(c *gin.Context) { + // Retrieve the uploaded file + uploadFile, err := c.FormFile("file") + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "failed to get file"}) + return + } + + // Open the uploaded file + file, err := uploadFile.Open() + if err != nil { + fmt.Println("Error opening uploaded file:", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open uploaded file"}) + return + } + + defer file.Close() + + + // Construct the FileRequest + fileReq := fstream.RFileRequest{ + File: file, + UploadFile: uploadFile, + MaxRange: rangeMax, + FileSize: fileSize, + UploadDirectory: "uploads/", + FileUniqueName: true, // remember that if you set true here, you will receive unique name file + } + + + // Call StoreChunk to handle the uploaded chunk + prFile, err := fstream.StoreChunk(&fileReq) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "failed to store chunk"}) + return + } + + // You can write your own if statement to check whatever you want + if isLast { + // You can perform your own logic here, before return 200 status, + // it's better to remove chunks which is uploaded and doesn't use anymore + fstream.RemoveUploadedFile(&fileReq) + } + + c.JSON(http.StatusOK, gin.H{"message": "file chunk processed"}) +} +``` + +`fstream` offers extension check, all you need is that to provide which extension do you want to allow + +```go + import( + "github.com/Nicolas-ggd/filestream" + ) + + // Declare extension slice, slice contains all file extension which is ok for you to allow in your system + var allowExtensions = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"} + + // This function is very simple, but save your time to compare extension and current file to known if it's valid for you + fstream.IsAllowExtension(allowExtensions, "filename.png") +``` ## License FileStream is open-source software licensed under the MIT License. diff --git a/cmd/app/app.go b/cmd/app/app.go deleted file mode 100644 index a11a549..0000000 --- a/cmd/app/app.go +++ /dev/null @@ -1,49 +0,0 @@ -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) - } -} diff --git a/cmd/app/config.go b/cmd/app/config.go deleted file mode 100644 index 2df5b8f..0000000 --- a/cmd/app/config.go +++ /dev/null @@ -1,15 +0,0 @@ -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 -} diff --git a/cmd/filestream/main.go b/cmd/filestream/main.go deleted file mode 100644 index 418ce79..0000000 --- a/cmd/filestream/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "github.com/Nicolas-ggd/filestream/cmd/app" - -func main() { - app.App() -} diff --git a/file.go b/file.go index 2957efb..adc0cf2 100644 --- a/file.go +++ b/file.go @@ -18,7 +18,7 @@ type File struct { // Original uploaded file name FileName string // FileUniqueName is unique name - FileUniqueName string + FileUniqueName *string // Uploaded file path FilePath string // Uploaded file extension @@ -43,8 +43,8 @@ type RFileRequest struct { FileUniqueName bool } -// generateUuidUniqueName function generates unique string using UUID -func (r *File) generateUuidUniqueName(request *RFileRequest) { +// uniqueName function generates unique string using UUID +func uniqueName(request *RFileRequest) string { ext := filepath.Ext(request.UploadFile.Filename) id, err := uuid.NewUUID() @@ -52,12 +52,16 @@ func (r *File) generateUuidUniqueName(request *RFileRequest) { log.Fatalln(err) } - r.FileUniqueName = fmt.Sprintf("%s%s", id.String(), ext) + return fmt.Sprintf("%s%s", id.String(), ext) } // RemoveUploadedFile function removes uploaded file from uploaded directory, it takes param and returns nothing: // -// RFileRequest struct +// Takes: +// +// - RFileRequest struct +// +// Use this function in your handler after file is uploaded func RemoveUploadedFile(r *RFileRequest) { filePath := filepath.Join(r.UploadDirectory, r.UploadFile.Filename) @@ -83,11 +87,11 @@ func prettyByteSize(b int) string { // StoreChunk cares slice of chunks and returns final results and error // -// File - struct is final version about file information +// - File - struct is final version about file information // -// error - functions cares about errors and returns error +// - error - functions cares about occurred errors and returns it. // -// 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 +// 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 @@ -114,7 +118,7 @@ func StoreChunk(r *RFileRequest) (*File, error) { }() // Copy the chunk data to the file - if _, err := io.Copy(f, r.File); err != nil { + if _, err = io.Copy(f, r.File); err != nil { return nil, fmt.Errorf("failed to copying file: %v", err) } @@ -128,13 +132,18 @@ func StoreChunk(r *RFileRequest) (*File, error) { // Calculate file size in bytes size := prettyByteSize(int(fileInfo.Size())) + // Check if FileUniqueName field is true to generate unique name for file + if r.FileUniqueName { + uName := uniqueName(r) + rFile.FileUniqueName = &uName + } + // 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, + FileName: r.UploadFile.Filename, + FilePath: filePath, + FileExtension: filepath.Ext(r.UploadFile.Filename), + FileSize: size, } } @@ -143,13 +152,13 @@ func StoreChunk(r *RFileRequest) (*File, error) { // 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 +// - 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" +// - 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 +// - 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)) diff --git a/file_test.go b/file_test.go new file mode 100644 index 0000000..e78dece --- /dev/null +++ b/file_test.go @@ -0,0 +1,8 @@ +package fstream + +import "testing" + +func FuzzStoreChunk(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + }) +} diff --git a/filestream b/filestream deleted file mode 100755 index 2ea2ae5..0000000 Binary files a/filestream and /dev/null differ