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 }