github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/objects/operations.go (about) 1 package objects 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/hex" 7 "fmt" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/qiniu/go-sdk/v7/storagev2/apis" 13 "github.com/qiniu/go-sdk/v7/storagev2/apis/stat_object" 14 ) 15 16 type ( 17 // 操作接口,用于发送单次请求或批量请求 18 Operation interface { 19 // 批量请求命令 20 fmt.Stringer 21 // 被操作的对象,必须返回至少一个 22 relatedEntries() []entry 23 // 处理返回结果 24 handleResponse(*ObjectDetails, error) 25 } 26 27 entry struct { 28 bucketName string 29 objectName string 30 } 31 32 // 获取对象元信息操作 33 StatObjectOperation struct { 34 object Object 35 needParts bool 36 onResponse func(*ObjectDetails) 37 onError func(error) 38 } 39 40 // 移动对象操作 41 MoveObjectOperation struct { 42 fromObject Object 43 toObject entry 44 force bool 45 onResponse func() 46 onError func(error) 47 } 48 49 // 复制对象操作 50 CopyObjectOperation struct { 51 fromObject Object 52 toObject entry 53 force bool 54 onResponse func() 55 onError func(error) 56 } 57 58 // 删除对象操作 59 DeleteObjectOperation struct { 60 object Object 61 onResponse func() 62 onError func(error) 63 } 64 65 // 解冻对象操作 66 RestoreObjectOperation struct { 67 object Object 68 freezeAfterDays int64 69 onResponse func() 70 onError func(error) 71 } 72 73 // 设置对象存储类型操作 74 SetObjectStorageClassOperation struct { 75 object Object 76 storageClass StorageClass 77 onResponse func() 78 onError func(error) 79 } 80 81 // 设置对象状态 82 SetObjectStatusOperation struct { 83 object Object 84 status Status 85 onResponse func() 86 onError func(error) 87 } 88 89 // 设置对象元信息操作 90 SetObjectMetadataOperation struct { 91 object Object 92 mimeType string 93 metadata map[string]string 94 conditions map[string]string 95 onResponse func() 96 onError func(error) 97 } 98 99 // 设置对象生命周期操作 100 SetObjectLifeCycleOperation struct { 101 object Object 102 toIAAfterDays int64 103 toArchiveIRAfterDays int64 104 toArchiveAfterDays int64 105 toDeepArchiveAfterDays int64 106 deleteAfterDays int64 107 onResponse func() 108 onError func(error) 109 } 110 ) 111 112 func (operation *StatObjectOperation) NeedParts(needParts bool) *StatObjectOperation { 113 copy := *operation 114 copy.needParts = needParts 115 return © 116 } 117 118 func (operation *StatObjectOperation) OnResponse(fn func(*ObjectDetails)) *StatObjectOperation { 119 copy := *operation 120 copy.onResponse = fn 121 return © 122 } 123 124 func (operation *StatObjectOperation) OnError(fn func(error)) *StatObjectOperation { 125 copy := *operation 126 copy.onError = fn 127 return © 128 } 129 130 func (operation *StatObjectOperation) relatedEntries() []entry { 131 return []entry{{operation.object.bucket.name, operation.object.name}} 132 } 133 134 func (operation *StatObjectOperation) parseResponse(response *stat_object.Response, err error) (*ObjectDetails, error) { 135 if err != nil { 136 if operation.onError != nil { 137 operation.onError(err) 138 } 139 return nil, err 140 } 141 142 object := ObjectDetails{ 143 Name: operation.object.name, 144 UploadedAt: time.Unix(response.PutTime/1e7, (response.PutTime%1e7)*1e2), 145 ETag: response.Hash, 146 Size: response.Size, 147 MimeType: response.MimeType, 148 StorageClass: StorageClass(response.Type), 149 EndUser: response.EndUser, 150 Status: Status(response.Status), 151 RestoreStatus: RestoreStatus(response.RestoringStatus), 152 Metadata: response.Metadata, 153 } 154 var md5 []byte 155 if response.Md5 != "" { 156 md5, err = hex.DecodeString(response.Md5) 157 if err != nil { 158 if operation.onError != nil { 159 operation.onError(err) 160 } 161 return nil, err 162 } 163 } 164 if len(md5) > 0 { 165 copy(object.MD5[:], md5) 166 } 167 if len(response.Parts) > 0 { 168 object.Parts = append(make(stat_object.PartSizes, 0, len(response.Parts)), response.Parts...) 169 } 170 if response.TransitionToIaTime > 0 { 171 transitionToIA := time.Unix(response.TransitionToIaTime, 0) 172 object.TransitionToIA = &transitionToIA 173 } 174 if response.TransitionToArchiveIrTime > 0 { 175 transitionToArchiveIR := time.Unix(response.TransitionToArchiveIrTime, 0) 176 object.TransitionToArchiveIR = &transitionToArchiveIR 177 } 178 if response.TransitionToArchiveTime > 0 { 179 transitionToArchive := time.Unix(response.TransitionToArchiveTime, 0) 180 object.TransitionToArchive = &transitionToArchive 181 } 182 if response.TransitionToDeepArchiveTime > 0 { 183 transitionToDeepArchive := time.Unix(response.TransitionToDeepArchiveTime, 0) 184 object.TransitionToDeepArchive = &transitionToDeepArchive 185 } 186 if response.ExpirationTime > 0 { 187 expireAt := time.Unix(response.ExpirationTime, 0) 188 object.ExpireAt = &expireAt 189 } 190 if operation.onResponse != nil { 191 operation.onResponse(&object) 192 } 193 return &object, nil 194 } 195 196 func (operation *StatObjectOperation) handleResponse(object *ObjectDetails, err error) { 197 if err != nil && operation.onError != nil { 198 operation.onError(err) 199 } else if operation.onResponse != nil { 200 operation.onResponse(object) 201 } 202 } 203 204 func (operation *StatObjectOperation) String() string { 205 s := "stat/" + operation.object.encode() 206 if operation.needParts { 207 s += "/needparts/true" 208 } 209 return s 210 } 211 212 func (operation *StatObjectOperation) Call(ctx context.Context) (*ObjectDetails, error) { 213 response, err := operation.object.bucket.objectsManager.storage.StatObject(ctx, &apis.StatObjectRequest{ 214 Entry: operation.object.String(), 215 NeedParts: operation.needParts, 216 }, &apis.Options{ 217 OverwrittenBucketName: operation.object.bucket.name, 218 }) 219 return operation.parseResponse(response, err) 220 } 221 222 var _ Operation = (*StatObjectOperation)(nil) 223 224 func (operation *MoveObjectOperation) Force(force bool) *MoveObjectOperation { 225 copy := *operation 226 copy.force = force 227 return © 228 } 229 230 func (operation *MoveObjectOperation) OnResponse(fn func()) *MoveObjectOperation { 231 copy := *operation 232 copy.onResponse = fn 233 return © 234 } 235 236 func (operation *MoveObjectOperation) OnError(fn func(error)) *MoveObjectOperation { 237 copy := *operation 238 copy.onError = fn 239 return © 240 } 241 242 func (operation *MoveObjectOperation) relatedEntries() []entry { 243 return []entry{{operation.fromObject.bucket.name, operation.fromObject.name}, operation.toObject} 244 } 245 246 func (operation *MoveObjectOperation) handleResponse(_ *ObjectDetails, err error) { 247 if err != nil && operation.onError != nil { 248 operation.onError(err) 249 } else if operation.onResponse != nil { 250 operation.onResponse() 251 } 252 } 253 254 func (operation *MoveObjectOperation) String() string { 255 s := "move/" + operation.fromObject.encode() + "/" + operation.toObject.encode() 256 if operation.force { 257 s += "/force/true" 258 } 259 return s 260 } 261 262 func (operation *MoveObjectOperation) Call(ctx context.Context) error { 263 _, err := operation.fromObject.bucket.objectsManager.storage.MoveObject(ctx, &apis.MoveObjectRequest{ 264 SrcEntry: operation.fromObject.String(), 265 DestEntry: operation.toObject.String(), 266 IsForce: operation.force, 267 }, &apis.Options{ 268 OverwrittenBucketName: operation.fromObject.bucket.name, 269 }) 270 operation.handleResponse(nil, err) 271 return err 272 } 273 274 var _ Operation = (*MoveObjectOperation)(nil) 275 276 func (operation *CopyObjectOperation) Force(force bool) *CopyObjectOperation { 277 copy := *operation 278 copy.force = force 279 return © 280 } 281 282 func (operation *CopyObjectOperation) OnResponse(fn func()) *CopyObjectOperation { 283 copy := *operation 284 copy.onResponse = fn 285 return © 286 } 287 288 func (operation *CopyObjectOperation) OnError(fn func(error)) *CopyObjectOperation { 289 copy := *operation 290 copy.onError = fn 291 return © 292 } 293 294 func (operation *CopyObjectOperation) relatedEntries() []entry { 295 return []entry{{operation.fromObject.bucket.name, operation.fromObject.name}, operation.toObject} 296 } 297 298 func (operation *CopyObjectOperation) handleResponse(_ *ObjectDetails, err error) { 299 if err != nil && operation.onError != nil { 300 operation.onError(err) 301 } else if operation.onResponse != nil { 302 operation.onResponse() 303 } 304 } 305 306 func (operation *CopyObjectOperation) String() string { 307 s := "copy/" + operation.fromObject.encode() + "/" + operation.toObject.encode() 308 if operation.force { 309 s += "/force/true" 310 } 311 return s 312 } 313 314 func (operation *CopyObjectOperation) Call(ctx context.Context) error { 315 _, err := operation.fromObject.bucket.objectsManager.storage.CopyObject(ctx, &apis.CopyObjectRequest{ 316 SrcEntry: operation.fromObject.String(), 317 DestEntry: operation.toObject.String(), 318 IsForce: operation.force, 319 }, &apis.Options{ 320 OverwrittenBucketName: operation.fromObject.bucket.name, 321 }) 322 operation.handleResponse(nil, err) 323 return err 324 } 325 326 var _ Operation = (*CopyObjectOperation)(nil) 327 328 func (operation *DeleteObjectOperation) OnResponse(fn func()) *DeleteObjectOperation { 329 copy := *operation 330 copy.onResponse = fn 331 return © 332 } 333 334 func (operation *DeleteObjectOperation) OnError(fn func(error)) *DeleteObjectOperation { 335 copy := *operation 336 copy.onError = fn 337 return © 338 } 339 340 func (operation *DeleteObjectOperation) handleResponse(_ *ObjectDetails, err error) { 341 if err != nil && operation.onError != nil { 342 operation.onError(err) 343 } else if operation.onResponse != nil { 344 operation.onResponse() 345 } 346 } 347 348 func (operation *DeleteObjectOperation) relatedEntries() []entry { 349 return []entry{{operation.object.bucket.name, operation.object.name}} 350 } 351 352 func (operation *DeleteObjectOperation) String() string { 353 return "delete/" + operation.object.encode() 354 } 355 356 func (operation *DeleteObjectOperation) Call(ctx context.Context) error { 357 _, err := operation.object.bucket.objectsManager.storage.DeleteObject(ctx, &apis.DeleteObjectRequest{ 358 Entry: operation.object.String(), 359 }, &apis.Options{ 360 OverwrittenBucketName: operation.object.bucket.name, 361 }) 362 operation.handleResponse(nil, err) 363 return err 364 } 365 366 var _ Operation = (*DeleteObjectOperation)(nil) 367 368 func (operation *RestoreObjectOperation) OnResponse(fn func()) *RestoreObjectOperation { 369 copy := *operation 370 copy.onResponse = fn 371 return © 372 } 373 374 func (operation *RestoreObjectOperation) OnError(fn func(error)) *RestoreObjectOperation { 375 copy := *operation 376 copy.onError = fn 377 return © 378 } 379 380 func (operation *RestoreObjectOperation) relatedEntries() []entry { 381 return []entry{{operation.object.bucket.name, operation.object.name}} 382 } 383 384 func (operation *RestoreObjectOperation) handleResponse(_ *ObjectDetails, err error) { 385 if err != nil && operation.onError != nil { 386 operation.onError(err) 387 } else if operation.onResponse != nil { 388 operation.onResponse() 389 } 390 } 391 392 func (operation *RestoreObjectOperation) String() string { 393 return "restoreAr/" + operation.object.encode() + "/freezeAfterDays/" + strconv.FormatInt(operation.freezeAfterDays, 10) 394 } 395 396 func (operation *RestoreObjectOperation) Call(ctx context.Context) error { 397 _, err := operation.object.bucket.objectsManager.storage.RestoreArchivedObject(ctx, &apis.RestoreArchivedObjectRequest{ 398 Entry: operation.object.String(), 399 FreezeAfterDays: int64(operation.freezeAfterDays), 400 }, &apis.Options{ 401 OverwrittenBucketName: operation.object.bucket.name, 402 }) 403 operation.handleResponse(nil, err) 404 return err 405 } 406 407 var _ Operation = (*RestoreObjectOperation)(nil) 408 409 func (operation *SetObjectStorageClassOperation) OnResponse(fn func()) *SetObjectStorageClassOperation { 410 copy := *operation 411 copy.onResponse = fn 412 return © 413 } 414 415 func (operation *SetObjectStorageClassOperation) OnError(fn func(error)) *SetObjectStorageClassOperation { 416 copy := *operation 417 copy.onError = fn 418 return © 419 } 420 421 func (operation *SetObjectStorageClassOperation) relatedEntries() []entry { 422 return []entry{{operation.object.bucket.name, operation.object.name}} 423 } 424 425 func (operation *SetObjectStorageClassOperation) handleResponse(_ *ObjectDetails, err error) { 426 if err != nil && operation.onError != nil { 427 operation.onError(err) 428 } else if operation.onResponse != nil { 429 operation.onResponse() 430 } 431 } 432 433 func (operation *SetObjectStorageClassOperation) String() string { 434 return "chtype/" + operation.object.encode() + "/type/" + strconv.Itoa(int(operation.storageClass)) 435 } 436 437 func (operation *SetObjectStorageClassOperation) Call(ctx context.Context) error { 438 _, err := operation.object.bucket.objectsManager.storage.SetObjectFileType(ctx, &apis.SetObjectFileTypeRequest{ 439 Entry: operation.object.String(), 440 Type: int64(operation.storageClass), 441 }, &apis.Options{ 442 OverwrittenBucketName: operation.object.bucket.name, 443 }) 444 operation.handleResponse(nil, err) 445 return err 446 } 447 448 var _ Operation = (*SetObjectStorageClassOperation)(nil) 449 450 func (operation *SetObjectStatusOperation) OnResponse(fn func()) *SetObjectStatusOperation { 451 copy := *operation 452 copy.onResponse = fn 453 return © 454 } 455 456 func (operation *SetObjectStatusOperation) OnError(fn func(error)) *SetObjectStatusOperation { 457 copy := *operation 458 copy.onError = fn 459 return © 460 } 461 462 func (operation *SetObjectStatusOperation) relatedEntries() []entry { 463 return []entry{{operation.object.bucket.name, operation.object.name}} 464 } 465 466 func (operation *SetObjectStatusOperation) handleResponse(_ *ObjectDetails, err error) { 467 if err != nil && operation.onError != nil { 468 operation.onError(err) 469 } else if operation.onResponse != nil { 470 operation.onResponse() 471 } 472 } 473 474 func (operation *SetObjectStatusOperation) String() string { 475 return "chstatus/" + operation.object.encode() + "/status/" + strconv.Itoa(int(operation.status)) 476 } 477 478 func (operation *SetObjectStatusOperation) Call(ctx context.Context) error { 479 _, err := operation.object.bucket.objectsManager.storage.ModifyObjectStatus(ctx, &apis.ModifyObjectStatusRequest{ 480 Entry: operation.object.String(), 481 Status: int64(operation.status), 482 }, &apis.Options{ 483 OverwrittenBucketName: operation.object.bucket.name, 484 }) 485 operation.handleResponse(nil, err) 486 return err 487 } 488 489 var _ Operation = (*SetObjectStatusOperation)(nil) 490 491 func (operation *SetObjectMetadataOperation) Metadata(metadata map[string]string) *SetObjectMetadataOperation { 492 copy := *operation 493 copy.metadata = metadata 494 return © 495 } 496 497 func (operation *SetObjectMetadataOperation) Conditions(conds map[string]string) *SetObjectMetadataOperation { 498 copy := *operation 499 copy.conditions = conds 500 return © 501 } 502 503 func (operation *SetObjectMetadataOperation) OnResponse(fn func()) *SetObjectMetadataOperation { 504 copy := *operation 505 copy.onResponse = fn 506 return © 507 } 508 509 func (operation *SetObjectMetadataOperation) OnError(fn func(error)) *SetObjectMetadataOperation { 510 copy := *operation 511 copy.onError = fn 512 return © 513 } 514 515 func (operation *SetObjectMetadataOperation) relatedEntries() []entry { 516 return []entry{{operation.object.bucket.name, operation.object.name}} 517 } 518 519 func (operation *SetObjectMetadataOperation) handleResponse(_ *ObjectDetails, err error) { 520 if err != nil && operation.onError != nil { 521 operation.onError(err) 522 } else if operation.onResponse != nil { 523 operation.onResponse() 524 } 525 } 526 527 func (operation *SetObjectMetadataOperation) String() string { 528 s := "chgm/" + operation.object.encode() + "/mime/" + base64.URLEncoding.EncodeToString([]byte(operation.mimeType)) 529 for k, v := range operation.metadata { 530 s += "/" + normalizeMetadataKey(k) + "/" + base64.URLEncoding.EncodeToString([]byte(v)) 531 } 532 conds := []string{} 533 for k, v := range operation.conditions { 534 conds = append(conds, k+"="+v) 535 } 536 if len(conds) > 0 { 537 s += "/cond/" + base64.URLEncoding.EncodeToString([]byte(strings.Join(conds, "&"))) 538 } 539 return s 540 } 541 542 func (operation *SetObjectMetadataOperation) Call(ctx context.Context) error { 543 conds := make([]string, 0, len(operation.conditions)) 544 for k, v := range operation.conditions { 545 conds = append(conds, k+"="+v) 546 } 547 metadata := make(map[string]string, len(operation.metadata)) 548 for k, v := range operation.metadata { 549 metadata[normalizeMetadataKey(k)] = v 550 } 551 _, err := operation.object.bucket.objectsManager.storage.ModifyObjectMetadata(ctx, &apis.ModifyObjectMetadataRequest{ 552 Entry: operation.object.String(), 553 MimeType: operation.mimeType, 554 Condition: strings.Join(conds, "&"), 555 MetaData: metadata, 556 }, &apis.Options{ 557 OverwrittenBucketName: operation.object.bucket.name, 558 }) 559 operation.handleResponse(nil, err) 560 return err 561 } 562 563 var _ Operation = (*SetObjectMetadataOperation)(nil) 564 565 func (operation *SetObjectLifeCycleOperation) ToIAAfterDays(afterDays int64) *SetObjectLifeCycleOperation { 566 operation.toIAAfterDays = afterDays 567 return operation 568 } 569 570 func (operation *SetObjectLifeCycleOperation) ToArchiveIRAfterDays(afterDays int64) *SetObjectLifeCycleOperation { 571 copy := *operation 572 copy.toArchiveIRAfterDays = afterDays 573 return © 574 } 575 576 func (operation *SetObjectLifeCycleOperation) ToArchiveAfterDays(afterDays int64) *SetObjectLifeCycleOperation { 577 copy := *operation 578 copy.toArchiveAfterDays = afterDays 579 return © 580 } 581 582 func (operation *SetObjectLifeCycleOperation) ToDeepArchiveAfterDays(afterDays int64) *SetObjectLifeCycleOperation { 583 copy := *operation 584 copy.toDeepArchiveAfterDays = afterDays 585 return © 586 } 587 588 func (operation *SetObjectLifeCycleOperation) DeleteAfterDays(afterDays int64) *SetObjectLifeCycleOperation { 589 copy := *operation 590 copy.deleteAfterDays = afterDays 591 return © 592 } 593 594 func (operation *SetObjectLifeCycleOperation) OnResponse(fn func()) *SetObjectLifeCycleOperation { 595 copy := *operation 596 copy.onResponse = fn 597 return © 598 } 599 600 func (operation *SetObjectLifeCycleOperation) OnError(fn func(error)) *SetObjectLifeCycleOperation { 601 copy := *operation 602 copy.onError = fn 603 return © 604 } 605 606 func (operation *SetObjectLifeCycleOperation) relatedEntries() []entry { 607 return []entry{{operation.object.bucket.name, operation.object.name}} 608 } 609 610 func (operation *SetObjectLifeCycleOperation) handleResponse(_ *ObjectDetails, err error) { 611 if err != nil && operation.onError != nil { 612 operation.onError(err) 613 } else if operation.onResponse != nil { 614 operation.onResponse() 615 } 616 } 617 618 func (operation *SetObjectLifeCycleOperation) String() string { 619 s := "lifecycle/" + operation.object.encode() 620 if operation.toIAAfterDays > 0 { 621 s += "/toIAAfterDays/" + strconv.FormatInt(operation.toIAAfterDays, 10) 622 } 623 if operation.toArchiveIRAfterDays > 0 { 624 s += "/toArchiveIRAfterDays/" + strconv.FormatInt(operation.toArchiveIRAfterDays, 10) 625 } 626 if operation.toArchiveAfterDays > 0 { 627 s += "/toArchiveAfterDays/" + strconv.FormatInt(operation.toArchiveAfterDays, 10) 628 } 629 if operation.toDeepArchiveAfterDays > 0 { 630 s += "/toDeepArchiveAfterDays/" + strconv.FormatInt(operation.toDeepArchiveAfterDays, 10) 631 } 632 if operation.deleteAfterDays > 0 { 633 s += "/deleteAfterDays/" + strconv.FormatInt(operation.deleteAfterDays, 10) 634 } 635 return s 636 } 637 638 func (operation *SetObjectLifeCycleOperation) Call(ctx context.Context) error { 639 _, err := operation.object.bucket.objectsManager.storage.ModifyObjectLifeCycle(ctx, &apis.ModifyObjectLifeCycleRequest{ 640 Entry: operation.object.String(), 641 ToIaAfterDays: operation.toIAAfterDays, 642 ToArchiveAfterDays: operation.toArchiveAfterDays, 643 ToDeepArchiveAfterDays: operation.toDeepArchiveAfterDays, 644 ToArchiveIrAfterDays: operation.toArchiveIRAfterDays, 645 DeleteAfterDays: operation.deleteAfterDays, 646 }, &apis.Options{ 647 OverwrittenBucketName: operation.object.bucket.name, 648 }) 649 operation.handleResponse(nil, err) 650 return err 651 } 652 653 var _ Operation = (*SetObjectLifeCycleOperation)(nil) 654 655 func (entry Object) String() string { 656 return entry.bucket.name + ":" + entry.name 657 } 658 659 func (entry Object) encode() string { 660 return base64.URLEncoding.EncodeToString([]byte(entry.String())) 661 } 662 663 func (entry entry) String() string { 664 return entry.bucketName + ":" + entry.objectName 665 } 666 667 func (entry entry) encode() string { 668 return base64.URLEncoding.EncodeToString([]byte(entry.String())) 669 } 670 671 func normalizeMetadataKey(k string) string { 672 if !strings.HasPrefix(k, "x-qn-meta-") { 673 k = "x-qn-meta-" + k 674 } 675 return k 676 }