|
|
@@ -0,0 +1,345 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "io/ioutil"
|
|
|
+ "math"
|
|
|
+ "os"
|
|
|
+ "path"
|
|
|
+ "path/filepath"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "github.com/FossoresLP/go-uuid-v4"
|
|
|
+)
|
|
|
+
|
|
|
+func check(e error) {
|
|
|
+ if e != nil {
|
|
|
+ panic(e)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//ArOZ PHP-Golang Bridge
|
|
|
+//The following sections remap the PHP functions to golang for the ease of development
|
|
|
+func file_exists(filepath string) bool {
|
|
|
+ if _, err := os.Stat(filepath); !os.IsNotExist(err) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func mkdir(filepath string) {
|
|
|
+ os.MkdirAll(filepath, os.ModePerm)
|
|
|
+}
|
|
|
+
|
|
|
+func file_put_contents(file string, data string) bool {
|
|
|
+ f, err := os.Create(file)
|
|
|
+ check(err)
|
|
|
+ _, err = f.WriteString(data)
|
|
|
+ defer f.Close()
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func file_get_contents(file string) string {
|
|
|
+ b, err := ioutil.ReadFile(file)
|
|
|
+ check(err)
|
|
|
+ return string(b)
|
|
|
+}
|
|
|
+
|
|
|
+func strtolower(word string) string {
|
|
|
+ return strings.ToLower(word)
|
|
|
+}
|
|
|
+
|
|
|
+func strtoupper(word string) string {
|
|
|
+ return strings.ToUpper(word)
|
|
|
+}
|
|
|
+
|
|
|
+func trim(word string) string {
|
|
|
+ return strings.Trim(word, " ")
|
|
|
+}
|
|
|
+
|
|
|
+func strlen(word string) int {
|
|
|
+ return len(word)
|
|
|
+}
|
|
|
+
|
|
|
+func count(array []string) int {
|
|
|
+ return len(array)
|
|
|
+}
|
|
|
+
|
|
|
+func explode(key string, word string) []string {
|
|
|
+ return strings.Split(word, key)
|
|
|
+}
|
|
|
+
|
|
|
+func implode(key string, array []string) string {
|
|
|
+ return strings.Join(array[:], key)
|
|
|
+}
|
|
|
+
|
|
|
+func str_replace(target string, result string, word string) string {
|
|
|
+ return strings.Replace(word, target, result, -1)
|
|
|
+}
|
|
|
+
|
|
|
+func in_array(a string, list []string) bool {
|
|
|
+ for _, b := range list {
|
|
|
+ if b == a {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func strpos(word string, target string) int {
|
|
|
+ return strings.Index(word, target)
|
|
|
+}
|
|
|
+
|
|
|
+func dirname(filepath string) string {
|
|
|
+ return path.Dir(filepath)
|
|
|
+}
|
|
|
+
|
|
|
+func basename(filepath string) string {
|
|
|
+ return path.Base(filepath)
|
|
|
+}
|
|
|
+
|
|
|
+//End of mapping functions
|
|
|
+//Utilities functions
|
|
|
+func genUUIDv4() string {
|
|
|
+ uuid, err := uuid.NewString()
|
|
|
+ check(err)
|
|
|
+ return uuid
|
|
|
+}
|
|
|
+
|
|
|
+func padZeros(thisInt string, maxval int) string {
|
|
|
+ targetLength := len(strconv.Itoa(maxval))
|
|
|
+ result := thisInt
|
|
|
+ if len(thisInt) < targetLength {
|
|
|
+ padzeros := targetLength - len(thisInt)
|
|
|
+ for i := 0; i < padzeros; i++ {
|
|
|
+ result = "0" + result
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+//End of utilities functions
|
|
|
+
|
|
|
+func init() {
|
|
|
+ //Check if the required directory exists. If not, create it.
|
|
|
+ if !file_exists("setting.config") {
|
|
|
+ setting, err := os.Create("setting.config")
|
|
|
+ check(err)
|
|
|
+ defer setting.Close()
|
|
|
+ setting.WriteString("")
|
|
|
+ setting.Sync()
|
|
|
+ }
|
|
|
+ if !file_exists("chunks/") {
|
|
|
+ mkdir("chunks/")
|
|
|
+ }
|
|
|
+ if !file_exists("uploads/") {
|
|
|
+ mkdir("uploads/")
|
|
|
+ }
|
|
|
+ if !file_exists("index/") {
|
|
|
+ mkdir("index/")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ //arozdfs implementation in Golang
|
|
|
+ /*
|
|
|
+ Supported commands:
|
|
|
+ help --> show all the help information
|
|
|
+
|
|
|
+ [Uploading to arozdfs commands]
|
|
|
+ slice
|
|
|
+ -infile <filename> --> declare the input file
|
|
|
+ -storepath <pathname> --> Relative path from the arozdfs root
|
|
|
+ -slice <filesize> --> declare the slicing filesize
|
|
|
+
|
|
|
+ upload
|
|
|
+ -push <clusterlist.config> --> push to a list of clusters and sync file index to other clusters
|
|
|
+
|
|
|
+ [Download from arozdfs commands]
|
|
|
+ download
|
|
|
+ -outfile <file.index> --> rebuild a file from cluster storage to local drive
|
|
|
+
|
|
|
+ open
|
|
|
+ -storepath <local path> --> the file chunks tmp folder
|
|
|
+ -uuid <file uuid> --> the uuid which the file is stored
|
|
|
+ -outfile <filename> --> filepath for the exported and merged file
|
|
|
+
|
|
|
+ [File Operations]
|
|
|
+ remove <file.index> --> remove all chunks related to thie file index
|
|
|
+ rename <file.index> <newfile.index> --> rename all records related to this file
|
|
|
+ move <filepath/file.index> <newpath/file.index> --> move the file to a new path in index directory
|
|
|
+
|
|
|
+
|
|
|
+ [System checking commands]
|
|
|
+ checkfile <file.index> --> check if a file contains all chunks which has at least two copies of each chunks
|
|
|
+ rebuild --> Check all files on the system and fix all chunks which has corrupted
|
|
|
+ migrate <host-uuid>
|
|
|
+
|
|
|
+ */
|
|
|
+
|
|
|
+ //For each argument, start the processing
|
|
|
+ switch functgroup := os.Args[1]; functgroup {
|
|
|
+ case "help":
|
|
|
+ showHelp()
|
|
|
+ case "slice":
|
|
|
+ startSlicingProc()
|
|
|
+ case "upload":
|
|
|
+ startUploadProc()
|
|
|
+ case "download":
|
|
|
+ startDownloadProc()
|
|
|
+ case "open":
|
|
|
+ openChunkedFile()
|
|
|
+ case "debug":
|
|
|
+ fmt.Println(padZeros("1", 32)) //Debug function. Change this line for unit testing
|
|
|
+ default:
|
|
|
+ showNotFound()
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ //Examples for using the Go-PHP bridge functions
|
|
|
+ file_put_contents("Hello World.txt", "This is the content of the file.")
|
|
|
+ fmt.Println(file_get_contents("Hello World.txt"))
|
|
|
+
|
|
|
+ array := explode(",", "Apple,Orange,Pizza")
|
|
|
+ fmt.Println(array)
|
|
|
+ newstring := implode(",", array)
|
|
|
+ fmt.Println(newstring)
|
|
|
+ fmt.Println(in_array("Pizza", array))
|
|
|
+ fmt.Println(strpos(newstring, "Pizza"))
|
|
|
+ fmt.Println(strtoupper("hello world"))
|
|
|
+ fmt.Println(str_replace("Pizza", "Ramen", newstring))
|
|
|
+ */
|
|
|
+}
|
|
|
+
|
|
|
+func startDownloadProc() {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func startSlicingProc() {
|
|
|
+ storepath := ""
|
|
|
+ infile := ""
|
|
|
+ slice := 64 //Default 64MB per file chunk
|
|
|
+ fileUUID := genUUIDv4()
|
|
|
+ for i, arg := range os.Args {
|
|
|
+ if strpos(arg, "-") == 0 {
|
|
|
+ //This is a parameter defining keyword
|
|
|
+ if arg == "-infile" {
|
|
|
+ infile = os.Args[i+1]
|
|
|
+ } else if arg == "-storepath" {
|
|
|
+ storepath = os.Args[i+1]
|
|
|
+ } else if arg == "-slice" {
|
|
|
+ sliceSize, err := strconv.Atoi(os.Args[i+1])
|
|
|
+ check(err)
|
|
|
+ slice = sliceSize
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if storepath != "" && infile != "" {
|
|
|
+ fmt.Println(storepath + " " + infile + " " + strconv.Itoa(slice) + " " + fileUUID)
|
|
|
+ splitFileChunks(infile, "chunks/"+storepath+"/", fileUUID, slice)
|
|
|
+ } else {
|
|
|
+ fmt.Println("ERROR. Undefined storepath or infile.")
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func splitFileChunks(rawfile string, outputdir string, outfilename string, chunksize int) bool {
|
|
|
+ if !file_exists(outputdir) {
|
|
|
+ mkdir(outputdir)
|
|
|
+ }
|
|
|
+ fileToBeChunked := rawfile
|
|
|
+ file, err := os.Open(fileToBeChunked)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ defer file.Close()
|
|
|
+
|
|
|
+ fileInfo, _ := file.Stat()
|
|
|
+
|
|
|
+ var fileSize int64 = fileInfo.Size()
|
|
|
+ var fileChunk = float64(chunksize * 1000 * 1000) // chunksize in MB
|
|
|
+
|
|
|
+ // calculate total number of parts the file will be chunked into
|
|
|
+
|
|
|
+ totalPartsNum := uint64(math.Ceil(float64(fileSize) / float64(fileChunk)))
|
|
|
+ fmt.Printf("Splitting to %d pieces.\n", totalPartsNum)
|
|
|
+ for i := uint64(0); i < totalPartsNum; i++ {
|
|
|
+ partSize := int(math.Min(fileChunk, float64(fileSize-int64(i*uint64(fileChunk)))))
|
|
|
+ partBuffer := make([]byte, partSize)
|
|
|
+ file.Read(partBuffer)
|
|
|
+ // write to disk
|
|
|
+ fileName := outputdir + outfilename + "_" + padZeros(strconv.FormatUint(i, 10), int(totalPartsNum))
|
|
|
+ _, err := os.Create(fileName)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ // write/save buffer to disk
|
|
|
+ ioutil.WriteFile(fileName, partBuffer, os.ModeAppend)
|
|
|
+ fmt.Println("Split to : ", fileName)
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func openChunkedFile() {
|
|
|
+ storepath := ""
|
|
|
+ uuid := ""
|
|
|
+ outfile := ""
|
|
|
+ for i, arg := range os.Args {
|
|
|
+ if strpos(arg, "-") == 0 {
|
|
|
+ //This is a parameter defining keyword
|
|
|
+ if arg == "-uuid" {
|
|
|
+ uuid = os.Args[i+1]
|
|
|
+ } else if arg == "-storepath" {
|
|
|
+ storepath = os.Args[i+1]
|
|
|
+ } else if arg == "-outfile" {
|
|
|
+ outfile = os.Args[i+1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if storepath != "" && uuid != "" && outfile != "" {
|
|
|
+ fmt.Println(storepath + " " + uuid + " " + outfile)
|
|
|
+ joinFileChunks(storepath+"/"+uuid, outfile)
|
|
|
+ } else {
|
|
|
+ fmt.Println("ERROR. Undefined storepath, outfile or uuid.")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func joinFileChunks(fileuuid string, outfilename string) bool {
|
|
|
+ matches, _ := filepath.Glob(fileuuid + "_*")
|
|
|
+ if len(matches) == 0 {
|
|
|
+ fmt.Println("ERROR. No filechunk file for this uuid.")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ outfile, err := os.Create(outfilename)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ //For each file chunk, merge them into the output file
|
|
|
+ for j := 0; j < len(matches); j++ {
|
|
|
+ b, _ := ioutil.ReadFile(matches[j])
|
|
|
+ outfile.Write(b)
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func startUploadProc() {
|
|
|
+ for i, arg := range os.Args {
|
|
|
+ // print index and value
|
|
|
+ fmt.Println("item", i, "is", arg)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func showHelp() {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func showNotFound() {
|
|
|
+ fmt.Println("ERROR. Command not found for function group: " + os.Args[1])
|
|
|
+}
|