github.com/sap/cf-mta-plugin@v2.6.3+incompatible/util/file_splitter.go (about) 1 package util 2 3 import ( 4 "fmt" 5 "io" 6 "math" 7 "os" 8 "path/filepath" 9 "strconv" 10 11 "github.com/cloudfoundry-incubator/multiapps-cli-plugin/configuration/properties" 12 "github.com/pborman/uuid" 13 ) 14 15 const MaxFileChunkCount = 50 16 17 func generateHash() string { 18 return uuid.New() 19 } 20 21 // SplitFile ... 22 func SplitFile(filePath string, fileChunkSizeInMB uint64) ([]string, error) { 23 if fileChunkSizeInMB == 0 { 24 return []string{filePath}, nil 25 } 26 27 file, err := os.Open(filePath) 28 if err != nil { 29 return nil, err 30 } 31 32 defer file.Close() 33 34 fileInfo, _ := file.Stat() 35 36 var fileSize = uint64(fileInfo.Size()) 37 var fileChunkSize = toBytes(fileChunkSizeInMB) 38 39 // calculate total number of parts the file will be chunked into 40 totalPartsNum := uint64(math.Ceil(float64(fileSize) / float64(fileChunkSize))) 41 if totalPartsNum <= 1 { 42 return []string{filePath}, nil 43 } 44 45 partsTempDir := filepath.Join(os.TempDir(), generateHash()) 46 errCreatingTempDir := os.MkdirAll(partsTempDir, os.ModePerm) 47 if errCreatingTempDir != nil { 48 return nil, errCreatingTempDir 49 } 50 51 baseFileName := filepath.Base(filePath) 52 var fileParts []string 53 54 for i := uint64(0); i < totalPartsNum; i++ { 55 filePartName := baseFileName + ".part." + strconv.FormatUint(i, 10) 56 tempFile := filepath.Join(partsTempDir, filePartName) 57 filePart, err := os.Create(tempFile) 58 if err != nil { 59 return nil, err 60 } 61 defer filePart.Close() 62 63 partSize := int64(minUint64(fileChunkSize, fileSize-i*fileChunkSize)) 64 _, err = io.CopyN(filePart, file, partSize) 65 if err != nil { 66 return nil, err 67 } 68 69 fileParts = append(fileParts, filePart.Name()) 70 } 71 return fileParts, nil 72 } 73 74 // ValidateChunkSize validate the chunk size 75 func ValidateChunkSize(filePath string, fileChunkSizeInMB uint64) error { 76 if fileChunkSizeInMB == 0 { 77 return nil 78 } 79 80 if fileChunkSizeInMB == properties.DefaultUploadChunkSizeInMB { 81 return nil 82 } 83 84 fileInfo, err := os.Stat(filePath) 85 if err != nil { 86 return err 87 } 88 89 var fileSize = uint64(fileInfo.Size()) 90 var fileSizeInMb = toMegabytes(fileSize) 91 92 var minFileChunkSizeInMb = uint64(math.Ceil(float64(fileSizeInMb) / float64(MaxFileChunkCount))) 93 94 if fileChunkSizeInMB < minFileChunkSizeInMb { 95 return fmt.Errorf("The specified chunk size (%d MB) is below the minimum chunk size (%d MB) for an archive with a size of %d MBs", fileChunkSizeInMB, minFileChunkSizeInMb, fileSizeInMb) 96 } 97 return nil 98 } 99 100 func minUint64(a, b uint64) uint64 { 101 if a < b { 102 return a 103 } 104 return b 105 } 106 107 func toBytes(mb uint64) uint64 { 108 return mb * 1024 * 1024 109 } 110 111 func toMegabytes(bytes uint64) uint64 { 112 return bytes / 1024 / 1024 113 }