Python拷贝文件

本文介绍如何在受版本控制的存储空间(Bucket)中拷贝文件(Object)。您可以通过CopyObject的方法拷贝小于1 GB的文件,通过分片拷贝(UploadPartCopy)的方法拷贝大于1 GB的文件。

注意事项

  • 本文以华东1(杭州)外网Endpoint为例。如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的RegionEndpoint的对应关系,请参见OSS地域和访问域名

  • 本文以OSS域名新建OSSClient为例。如果您希望通过自定义域名、STS等方式新建OSSClient,请参见初始化

  • 要拷贝文件,您必须有oss:GetObjectoss:PutObject权限。具体操作,请参见RAM用户授权自定义的权限策略

拷贝小文件

对于小于1 GB的文件,您可以通过CopyObject方法将文件从一个存储空间(源存储空间)复制到同一地域的另一个存储空间(目标存储空间)。

  • x-oss-copy-source默认拷贝Object的当前版本。如果当前版本是删除标记,则返回404表示该Object不存在。您可以在x-oss-copy-source中加入versionId来拷贝指定的Object版本,删除标记不能被拷贝。

  • 您可以将Object的早期版本拷贝到同一个Bucket中,拷贝Object的历史版本将会成为一个新的当前版本,达到恢复Object早期版本的目的。

  • 如果目标Bucket已开启版本控制,OSS将会为新拷贝出来的Object自动生成唯一的版本ID,此版本ID将会在响应headerx-oss-version-id中返回。如果目标Bucket未开启或者暂停了版本控制,OSS将会为新拷贝的Object自动生成version ID为“null”的版本,且会覆盖原先versionId为“null”的版本。

  • 当目标Bucket未开启版本控制时,支持对Appendable类型Object执行拷贝操作;当目标Bucket开启或暂停版本控制状态时,不支持对Appendable类型Object执行拷贝操作。

# -*- coding: utf-8 -*-
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())

# 填写源Bucket名称,例如srcexamplebucket。
src_bucket_name = 'srcexamplebucket'
# 填写与源Bucket处于同一地域的目标Bucket名称,例如destexamplebucket。
# 当在同一Bucket内拷贝文件时,请确保源Bucket名称和目标Bucket名称相同。
dest_bucket_name = 'destexamplebucket'

# 填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
# 填写Endpoint对应的Region信息,例如cn-hangzhou。注意,v4签名下,必须填写该参数
region = "cn-hangzhou"
bucket = oss2.Bucket(auth, endpoint, dest_bucket_name, region=region)

# 填写源Object完整路径,完整路径中不能包含Bucket名称,例如srcexampledir/exampleobject.txt。
src_object_name = 'srcexampledir/exampleobject.txt'
# 填写目标Object完整路径,完整路径中不能包含Bucket名称,例如destexampledir/exampleobject.txt。
dest_object_name = 'destexampledir/exampleobject.txt'
# 填写Object的版本ID。
versionId = 'CAEQMxiBgICAof2D0BYiIDJhMGE3N2M1YTI1NDQzOGY5NTkyNTI3MGYyMzJm****'

# 拷贝指定版本的文件。
params = dict()
params['versionId'] = versionId

# 从源Bucket中拷贝一个Object到目标Bucket。
result = bucket.copy_object(src_bucket_name, src_object_name, dest_object_name, params=params)
# 查看返回结果的状态,如果返回值为200时,表示执行成功。
print('result.status:', result.status)

拷贝大文件

对于大于1 GB的文件,需要使用分片拷贝(UploadPartCopy)。

UploadPartCopy默认从一个已存在的Object的当前版本中拷贝数据来上传一个Part。允许通过在请求header : x-oss-copy-source中附带versionId的子条件,实现从Object的指定版本进行拷贝,例如x-oss-copy-source : /SourceBucketName/SourceObjectName?versionId=CAEQMxiBgICAof2D0BYiIDJhMGE3N2M1YTI1NDQzOGY5NTkyNTI3MGYyMzJm****。

说明

SourceObjectName要进行URL编码。响应中将会返回被拷贝的Object版本ID,即x-oss-copy-source-version-id。

如果未指定versionId且拷贝Object的当前版本为删除标记,OSS将返回404 Not Found。通过指定versionId来拷贝删除标记时,OSS将返回400 Bad Request。

以下代码用于分片拷贝。

# -*- coding: utf-8 -*-
import os
import oss2
from oss2 import determine_part_size
from oss2.models import PartInfo
from oss2.credentials import EnvironmentVariableCredentialsProvider

# 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())

# 填写源Bucket名称,例如srcexamplebucket。
src_bucket_name = 'srcexamplebucket'
# 填写与源Bucket处于同一地域的目标Bucket名称,例如destexamplebucket。
# 当在同一Bucket内拷贝文件时,请确保源Bucket名称和目标Bucket名称相同。
dest_bucket_name = 'destexamplebucket'

# 填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
# 填写Endpoint对应的Region信息,例如cn-hangzhou。注意,v4签名下,必须填写该参数
region = "cn-hangzhou"
bucket = oss2.Bucket(auth, endpoint, dest_bucket_name, region=region)

# 当在同一Bucket内拷贝文件时,请注释掉该行代码,并将后面的src_bucket改为bucket即可。
src_bucket = oss2.Bucket(auth, endpoint, src_bucket_name, region=region)

# 填写源Object完整路径,完整路径中不能包含Bucket名称,例如srcexampledir/exampleobject.txt。
src_object_name = 'srcexampledir/exampleobject.txt'
# 填写目标Object完整路径,完整路径中不能包含Bucket名称,例如destexampledir/exampleobject.txt。
dest_object_name = 'destexampledir/exampleobject.txt'
# 填写Object的版本ID。
versionId = 'CAEQQhiBgMC5kZDE4RciIGZmNzdlYzRjYzRkODQwYjQ5ZDE0Njg5MWM0ODQzNDg3'

params = dict()
params['versionId'] = versionId

# 获取指定版本的源文件的文件大小。当在同一Bucket内拷贝文件时,请将src_bucket改为bucket。
head_info = src_bucket.head_object(src_object_name, params=params)
total_size = head_info.content_length
print('src object size:', total_size)

# determine_part_size方法用来确定分片大小。
part_size = determine_part_size(total_size, preferred_size=100 * 1024)
print('part_size:', part_size)

# 初始化分片。
upload_id = bucket.init_multipart_upload(dest_object_name).upload_id
parts = []

# 逐个上传分片。
part_number = 1
offset = 0
while offset < total_size:
    num_to_upload = min(part_size, total_size - offset)
    end = offset + num_to_upload - 1
    result = bucket.upload_part_copy(src_bucket_name, src_object_name, (offset, end), dest_object_name, upload_id, part_number, params=params)
    # 保存part信息。
    parts.append(PartInfo(part_number, result.etag))

    offset += num_to_upload
    part_number += 1

# 完成分片上传。
result = bucket.complete_multipart_upload(dest_object_name, upload_id, parts)
# 查看拷贝生成的目标Object的versionId。
print('result version id:', result.versionid)
# 获取文件元数据。
head_info = bucket.head_object(dest_object_name)
# 查看目标Object大小。
dest_object_size = head_info.content_length
print('dest object size:', dest_object_size)
# 对比源Object和目标Object的大小。
assert dest_object_size == total_size

相关文档

  • 关于拷贝小文件的更多信息,请参见CopyObject

  • 关于拷贝大文件的更多信息,请参见UploadPartCopy