复制对象

复制对象

CopyObject操作用于创建一个已经在媒体存储中的对象。使用CopyOoject可以拷贝单个最大为5GB的对象,如果需要拷贝更大的对象,可以使用UploadPartCopy操作。执行CopyObject操作,必须具有对被拷贝对象的READ权限和对目标bucket的WRITE权限。

拷贝生成的对象默认保留原对象的元数据信息,也可以才CopyObject操作中指定新的元数据。拷贝生成的对象不会保留原来的ACL信息,该对象默认是发起CopyObject操作的用户私有的。

CopyObject操作默认拷贝原对象的当前版本数据,如果需要拷贝原对象的指定版本,可以在CopySource中加入versionId来拷贝指定的Object版本,如果原对象的versionId为删除标记,则不会被拷贝。如果目标bucket开启了版本控制,那么拷贝生成的对象会有一个与原对象不同的唯一的versionId,并且会在响应头部字段 x-amz-version-id中返回该versionId。

代码示例:

// 拷贝对象
func CopyObject(svc *s3.S3) {
    copyObjectInput := &s3.CopyObjectInput{
        Bucket:     aws.String("destinationBucket"),
        CopySource: aws.String("exampleBucket/exampleKey"),
        Key:        aws.String("copiedObjectKey"),
    }

    copyObjectOutput, err := svc.CopyObject(copyObjectInput)
    if err != nil {
        fmt.Printf("fail to copy object. %v\n", err)
    }
    fmt.Println(copyObjectOutput)
}

CopyObjectInput可设置的参数如下:

参数 类型 说明 是否必要
ACL *string 拷贝后对象的预定义的标准ACL信息,例如private,public-read,public-read-write等。
Bucket *string 存放拷贝生成对象的桶名称。
CopySource *string URL格式的拷贝对象数据来源,包含了桶名称和对象key的信息,二者之间使用正斜杆(/)分割,versionId可选参数用于指定原对象的版本。例如,“foo/boo?versionId=11111"表示拷贝foo桶中的boo对象,其版本id为11111。如果不指定versionId参数,则默认拷贝当前版本的对象数据。
Key *string 拷贝生成对象对应的key。
Metadata map[string]*string 拷贝生成对象的元数据信息。
Tagging *string 拷贝生成对象的标签信息,必须是URL请求参数的形式。例如,“Key1=Value1”。

CopyObjectOutput返回的属性如下:

参数 类型 说明
CopyObjectResult *CopyObjectResult 包含拷贝生成对象的Entity Tag和最后修改时间等信息。
RequestCharged *string 如果返回结果中包含该属性,表示请求的发起者对该次请求操作付费。

响应结果:

HTTP状态 响应码 描述
200 Success 操作成功。
400 EntityTooLarge 拷贝的对象太大。
400 InvalidObjectName 对象的名字不合法
403 AccessDenied 用户没有权限执行操作。
404 NoSuchKey 请求参数中CopySource对应的对象不存在。

大文件分片复制

文件比较大(超过1GB)的情况下,直接使用copyObject 可能会出现超时,需要使用分片复制的方式进行文件复制。

代码示例:

const max_part_size = 5 * 1024 * 1024

func (p *S3Demo) buildCopySourceRange(start int64, objectSize int64) string {
    end := start + max_part_size - 1
    if end > objectSize {
        end = objectSize - 1
    }
    startRange := strconv.FormatInt(start, 10)
    stopRange := strconv.FormatInt(end, 10)
    return "bytes=" + startRange + "-" + stopRange
}

func (p *S3Demo) multiPartCopy() {
    sourceBucket := S3Bucket
    sourceKey := "2.avi"
    destBucket := S3Bucket
    destKey := "3.avi"

    headOut, err := p.svc.HeadObject(&s3.HeadObjectInput{
        Bucket: aws.String(sourceBucket),
        Key:    aws.String(sourceKey),
    })
    if err != nil {
        log.Printf("err, %s\n", err)
        return
    }
    fileSize := *headOut.ContentLength

    //send command to start copy and get the upload id as it is needed later
    createOutput, err := p.svc.CreateMultipartUpload(&s3.CreateMultipartUploadInput{
        Bucket: &destBucket,
        Key:    &destKey,
    })
    if err != nil {
        log.Printf("err, %s\n", err)
        return
    }
    uploadId := *createOutput.UploadId

    partNumber := int64(1)
    copySource := "/" + sourceBucket + "/" + sourceKey
    parts := make([]*s3.CompletedPart, 0)
    numUploads := fileSize / max_part_size
    log.Printf("Will attempt upload in %d number of parts to %s\n", numUploads, destKey)
    for i := int64(0); i < fileSize; i += max_part_size {
        copyRange := p.buildCopySourceRange(i, fileSize)
        partInput := s3.UploadPartCopyInput{
            Bucket:          &destBucket,
            CopySource:      &copySource,
            CopySourceRange: &copyRange,
            Key:             &destKey,
            PartNumber:      &partNumber,
            UploadId:        &uploadId,
        }
        log.Printf("Attempting to upload part %d range: %s\n", partNumber, copyRange)
        partResp, err := p.svc.UploadPartCopy(&partInput)

        if err != nil {
            log.Printf("Error uploading part %d : %s\n", partNumber, err)
            log.Printf("Attempting to abort upload\n")
            //ignoring any errors with aborting the copy
            p.svc.AbortMultipartUploadRequest(&s3.AbortMultipartUploadInput{
                UploadId: &uploadId,
            })
            return
        }

        //copy etag and part number from response as it is needed for completion
        if partResp != nil {
            partNum := partNumber
            etag := strings.Trim(*partResp.CopyPartResult.ETag, "\"")
            cPart := s3.CompletedPart{
                ETag:       &etag,
                PartNumber: &partNum,
            }
            parts = append(parts, &cPart)
            log.Printf("Successfully upload part %d of %s\n", partNumber, uploadId)
        }
        partNumber++
        if partNumber%50 == 0 {
            log.Printf("Completed part %d of %d to %s\n", partNumber, numUploads, destKey)
        }
    }

    // complete
    complete := s3.CompleteMultipartUploadInput{
        Bucket:   &destBucket,
        Key:      &destKey,
        UploadId: &uploadId,
        MultipartUpload: &s3.CompletedMultipartUpload{
            Parts: parts,
        },
    }
    compOutput, err := p.svc.CompleteMultipartUpload(&complete)
    if err != nil {
        log.Printf("Error completing upload: %s\n", err)
        return
    }
    if compOutput != nil {
        log.Printf("Successfully copied Bucket: %s Key: %s to Bucket: %s Key: %s\n",
            sourceBucket, sourceKey, destBucket, destKey)
    }
}