1package oss 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "hash/crc64" 8 "net/http" 9 "os" 10 "os/exec" 11 "runtime" 12 "strconv" 13 "strings" 14 "time" 15) 16 17// userAgent gets user agent 18// It has the SDK version information, OS information and GO version 19func userAgent() string { 20 sys := getSysInfo() 21 return fmt.Sprintf("aliyun-sdk-go/%s (%s/%s/%s;%s)", Version, sys.name, 22 sys.release, sys.machine, runtime.Version()) 23} 24 25type sysInfo struct { 26 name string // OS name such as windows/Linux 27 release string // OS version 2.6.32-220.23.2.ali1089.el5.x86_64 etc 28 machine string // CPU type amd64/x86_64 29} 30 31// getSysInfo gets system info 32// gets the OS information and CPU type 33func getSysInfo() sysInfo { 34 name := runtime.GOOS 35 release := "-" 36 machine := runtime.GOARCH 37 if out, err := exec.Command("uname", "-s").CombinedOutput(); err == nil { 38 name = string(bytes.TrimSpace(out)) 39 } 40 if out, err := exec.Command("uname", "-r").CombinedOutput(); err == nil { 41 release = string(bytes.TrimSpace(out)) 42 } 43 if out, err := exec.Command("uname", "-m").CombinedOutput(); err == nil { 44 machine = string(bytes.TrimSpace(out)) 45 } 46 return sysInfo{name: name, release: release, machine: machine} 47} 48 49// unpackedRange 50type unpackedRange struct { 51 hasStart bool // Flag indicates if the start point is specified 52 hasEnd bool // Flag indicates if the end point is specified 53 start int64 // Start point 54 end int64 // End point 55} 56 57// invalidRangeError returns invalid range error 58func invalidRangeError(r string) error { 59 return fmt.Errorf("InvalidRange %s", r) 60} 61 62// parseRange parse various styles of range such as bytes=M-N 63func parseRange(normalizedRange string) (*unpackedRange, error) { 64 var err error 65 hasStart := false 66 hasEnd := false 67 var start int64 68 var end int64 69 70 // Bytes==M-N or ranges=M-N 71 nrSlice := strings.Split(normalizedRange, "=") 72 if len(nrSlice) != 2 || nrSlice[0] != "bytes" { 73 return nil, invalidRangeError(normalizedRange) 74 } 75 76 // Bytes=M-N,X-Y 77 rSlice := strings.Split(nrSlice[1], ",") 78 rStr := rSlice[0] 79 80 if strings.HasSuffix(rStr, "-") { // M- 81 startStr := rStr[:len(rStr)-1] 82 start, err = strconv.ParseInt(startStr, 10, 64) 83 if err != nil { 84 return nil, invalidRangeError(normalizedRange) 85 } 86 hasStart = true 87 } else if strings.HasPrefix(rStr, "-") { // -N 88 len := rStr[1:] 89 end, err = strconv.ParseInt(len, 10, 64) 90 if err != nil { 91 return nil, invalidRangeError(normalizedRange) 92 } 93 if end == 0 { // -0 94 return nil, invalidRangeError(normalizedRange) 95 } 96 hasEnd = true 97 } else { // M-N 98 valSlice := strings.Split(rStr, "-") 99 if len(valSlice) != 2 { 100 return nil, invalidRangeError(normalizedRange) 101 } 102 start, err = strconv.ParseInt(valSlice[0], 10, 64) 103 if err != nil { 104 return nil, invalidRangeError(normalizedRange) 105 } 106 hasStart = true 107 end, err = strconv.ParseInt(valSlice[1], 10, 64) 108 if err != nil { 109 return nil, invalidRangeError(normalizedRange) 110 } 111 hasEnd = true 112 } 113 114 return &unpackedRange{hasStart, hasEnd, start, end}, nil 115} 116 117// adjustRange returns adjusted range, adjust the range according to the length of the file 118func adjustRange(ur *unpackedRange, size int64) (start, end int64) { 119 if ur == nil { 120 return 0, size 121 } 122 123 if ur.hasStart && ur.hasEnd { 124 start = ur.start 125 end = ur.end + 1 126 if ur.start < 0 || ur.start >= size || ur.end > size || ur.start > ur.end { 127 start = 0 128 end = size 129 } 130 } else if ur.hasStart { 131 start = ur.start 132 end = size 133 if ur.start < 0 || ur.start >= size { 134 start = 0 135 } 136 } else if ur.hasEnd { 137 start = size - ur.end 138 end = size 139 if ur.end < 0 || ur.end > size { 140 start = 0 141 end = size 142 } 143 } 144 return 145} 146 147// GetNowSec returns Unix time, the number of seconds elapsed since January 1, 1970 UTC. 148// gets the current time in Unix time, in seconds. 149func GetNowSec() int64 { 150 return time.Now().Unix() 151} 152 153// GetNowNanoSec returns t as a Unix time, the number of nanoseconds elapsed 154// since January 1, 1970 UTC. The result is undefined if the Unix time 155// in nanoseconds cannot be represented by an int64. Note that this 156// means the result of calling UnixNano on the zero Time is undefined. 157// gets the current time in Unix time, in nanoseconds. 158func GetNowNanoSec() int64 { 159 return time.Now().UnixNano() 160} 161 162// GetNowGMT gets the current time in GMT format. 163func GetNowGMT() string { 164 return time.Now().UTC().Format(http.TimeFormat) 165} 166 167// FileChunk is the file chunk definition 168type FileChunk struct { 169 Number int // Chunk number 170 Offset int64 // Chunk offset 171 Size int64 // Chunk size. 172} 173 174// SplitFileByPartNum splits big file into parts by the num of parts. 175// Split the file with specified parts count, returns the split result when error is nil. 176func SplitFileByPartNum(fileName string, chunkNum int) ([]FileChunk, error) { 177 if chunkNum <= 0 || chunkNum > 10000 { 178 return nil, errors.New("chunkNum invalid") 179 } 180 181 file, err := os.Open(fileName) 182 if err != nil { 183 return nil, err 184 } 185 defer file.Close() 186 187 stat, err := file.Stat() 188 if err != nil { 189 return nil, err 190 } 191 192 if int64(chunkNum) > stat.Size() { 193 return nil, errors.New("oss: chunkNum invalid") 194 } 195 196 var chunks []FileChunk 197 var chunk = FileChunk{} 198 var chunkN = (int64)(chunkNum) 199 for i := int64(0); i < chunkN; i++ { 200 chunk.Number = int(i + 1) 201 chunk.Offset = i * (stat.Size() / chunkN) 202 if i == chunkN-1 { 203 chunk.Size = stat.Size()/chunkN + stat.Size()%chunkN 204 } else { 205 chunk.Size = stat.Size() / chunkN 206 } 207 chunks = append(chunks, chunk) 208 } 209 210 return chunks, nil 211} 212 213// SplitFileByPartSize splits big file into parts by the size of parts. 214// Splits the file by the part size. Returns the FileChunk when error is nil. 215func SplitFileByPartSize(fileName string, chunkSize int64) ([]FileChunk, error) { 216 if chunkSize <= 0 { 217 return nil, errors.New("chunkSize invalid") 218 } 219 220 file, err := os.Open(fileName) 221 if err != nil { 222 return nil, err 223 } 224 defer file.Close() 225 226 stat, err := file.Stat() 227 if err != nil { 228 return nil, err 229 } 230 var chunkN = stat.Size() / chunkSize 231 if chunkN >= 10000 { 232 return nil, errors.New("Too many parts, please increase part size") 233 } 234 235 var chunks []FileChunk 236 var chunk = FileChunk{} 237 for i := int64(0); i < chunkN; i++ { 238 chunk.Number = int(i + 1) 239 chunk.Offset = i * chunkSize 240 chunk.Size = chunkSize 241 chunks = append(chunks, chunk) 242 } 243 244 if stat.Size()%chunkSize > 0 { 245 chunk.Number = len(chunks) + 1 246 chunk.Offset = int64(len(chunks)) * chunkSize 247 chunk.Size = stat.Size() % chunkSize 248 chunks = append(chunks, chunk) 249 } 250 251 return chunks, nil 252} 253 254// GetPartEnd calculates the end position 255func GetPartEnd(begin int64, total int64, per int64) int64 { 256 if begin+per > total { 257 return total - 1 258 } 259 return begin + per - 1 260} 261 262// crcTable returns the table constructed from the specified polynomial 263var crcTable = func() *crc64.Table { 264 return crc64.MakeTable(crc64.ECMA) 265} 266