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  }