github.com/kaydxh/golang@v0.0.131/pkg/file-transfer/upload/upload.svr.go (about)

     1  /*
     2   *Copyright (c) 2022, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package upload
    23  
    24  import (
    25  	"fmt"
    26  	"io"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  
    31  	md5_ "github.com/kaydxh/golang/go/crypto/md5"
    32  	io_ "github.com/kaydxh/golang/go/io"
    33  	"github.com/kaydxh/golang/go/sync/atomic"
    34  	atomic_ "github.com/kaydxh/golang/go/sync/atomic"
    35  )
    36  
    37  type UploadPartInput struct {
    38  	PartId uint32
    39  	Offset int64
    40  	Length int64
    41  	Md5Sum string
    42  }
    43  
    44  const tmpFileSuffix = "_.tmp"
    45  
    46  func UploadFile(
    47  	r io.Reader,
    48  	filePath string,
    49  	md5Sum string,
    50  ) error {
    51  	if filePath == "" {
    52  		return fmt.Errorf("invalid filePath")
    53  	}
    54  
    55  	//check path
    56  	absFilePath, err := filepath.Abs(filePath)
    57  	if strings.Contains(absFilePath, "..") {
    58  		err = fmt.Errorf("invalid filePath: %v", absFilePath)
    59  		return err
    60  	}
    61  
    62  	var mu atomic_.FileLock = atomic.FileLock(filepath.Join("./", absFilePath))
    63  	err = mu.TryLock()
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	unlockFun := func() error {
    69  		err = mu.TryUnLock()
    70  		if err != nil {
    71  			return err
    72  		}
    73  		return nil
    74  	}
    75  	defer unlockFun()
    76  
    77  	if md5Sum != "" {
    78  		sum, err := md5_.SumReader(r)
    79  		if err != nil {
    80  			return err
    81  		}
    82  		gotSum := strings.ToLower(sum)
    83  		expectSum := strings.ToLower(md5Sum)
    84  
    85  		if gotSum != expectSum {
    86  			return fmt.Errorf(
    87  				"failed to check md5Sum, got: %v, expect: %v",
    88  				gotSum,
    89  				expectSum,
    90  			)
    91  		}
    92  
    93  	}
    94  
    95  	err = io_.WriteReader(filePath, r)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func UploadMultipart(
   104  	r io.Reader,
   105  	partInput *UploadPartInput,
   106  	filePath string,
   107  ) error {
   108  	if partInput == nil {
   109  		return fmt.Errorf("invalid partInput")
   110  	}
   111  	if filePath == "" {
   112  		return fmt.Errorf("invalid filePath")
   113  	}
   114  
   115  	//check path
   116  	absFilePath, err := filepath.Abs(filePath)
   117  	if strings.Contains(absFilePath, "..") {
   118  		err = fmt.Errorf("invalid filePath: %v", absFilePath)
   119  		return err
   120  	}
   121  
   122  	var mu atomic_.FileLock = atomic.FileLock(filepath.Join("./", absFilePath))
   123  	err = mu.TryLock()
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	unlockFun := func() error {
   129  		err = mu.TryUnLock()
   130  		if err != nil {
   131  			return err
   132  		}
   133  		return nil
   134  	}
   135  	defer unlockFun()
   136  
   137  	if partInput.Md5Sum != "" {
   138  		sum, err := md5_.SumReader(r)
   139  		if err != nil {
   140  			return err
   141  		}
   142  		gotSum := strings.ToLower(sum)
   143  		expectSum := strings.ToLower(partInput.Md5Sum)
   144  
   145  		if gotSum != expectSum {
   146  			return fmt.Errorf(
   147  				"failed to check md5Sum, got: %v, expect: %v",
   148  				gotSum,
   149  				expectSum,
   150  			)
   151  		}
   152  
   153  	}
   154  
   155  	tmpFilePath := filePath + tmpFileSuffix
   156  	err = io_.WriteReaderAt(
   157  		tmpFilePath,
   158  		r,
   159  		partInput.Offset,
   160  		partInput.Length,
   161  	)
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	return nil
   167  }
   168  
   169  func CompleteMultipartUpload(
   170  	filePath string,
   171  	md5Sum string) error {
   172  	if filePath == "" {
   173  		return fmt.Errorf("invalid filePath")
   174  	}
   175  
   176  	tmpFilePath := filePath + tmpFileSuffix
   177  	if md5Sum != "" {
   178  		sum, err := md5_.SumFile(tmpFilePath)
   179  		if err != nil {
   180  			return err
   181  		}
   182  
   183  		gotSum := strings.ToLower(sum)
   184  		expectSum := strings.ToLower(md5Sum)
   185  
   186  		if gotSum != expectSum {
   187  			return fmt.Errorf(
   188  				"failed to check md5Sum, got: %v, expect: %v",
   189  				gotSum,
   190  				expectSum,
   191  			)
   192  		}
   193  	}
   194  
   195  	_, err := os.Stat(tmpFilePath)
   196  	if err != nil {
   197  		return fmt.Errorf("file: %v is not existed", tmpFilePath)
   198  	}
   199  
   200  	err = os.Rename(tmpFilePath, filePath)
   201  	if err != nil {
   202  		return fmt.Errorf("failed to Rename: %v to %v", tmpFilePath, filePath)
   203  	}
   204  
   205  	return nil
   206  }