# coding=utf-8
import math, os
from ks3.connection import Connection
from filechunkio import FileChunkIO

####################公共头#########################
# 金山云主账号 AccessKey 拥有所有API的访问权限,风险很高。强烈建议您创建并使用子账号账号进行 API 访问或日常运维,请登录 https://uc.console.ksyun.com/pro/iam/#/user/list 创建子账号。
# 通过指定 host(Endpoint),您可以在指定的地域创建新的存储空间。host(Endpoint) 以北京为例,其它 Region 请按实际情况填写。
conn = Connection('<yourAccessKeyId>', '<yourAccessKeySecret>', host='ks3-cn-beijing.ksyuncs.com')
# 获取存储空间实例
b = conn.get_bucket('<yourBucketName>')
# 源文件路径
source_path = '<yourSourceFilePath>'
# 源文件大小
source_size = os.stat(source_path).st_size
# 对象键名
key_name = '<yourKeyName>'

# 初始化分片。获取初始化的 uploadId,之后的操作中将会用到
# 如需在初始化分片时设置文件存储类型,请在 initiate_multipart_upload 中设置相关 headers
# x-kss-storage-class有效值为"STANDARD"、"STANDARD_IA"。"STANDARD"表示标准存储,"STANDARD_IA"表示低频存储,如果不指定,默认为标准存储。
headers = {"x-kss-storage-class": "STANDARD"}
mp = b.initiate_multipart_upload(key_name, headers=headers)
upload_id = mp.id

# 举例以 50 MiB 为分片大小
chunk_size = 52428800
chunk_count = int(math.ceil(source_size*1.0 / chunk_size*1.0))

# 通过 FileChunkIO 将文件分片并逐个上传
for i in range(chunk_count):
    offset = chunk_size * i
    bytes = min(chunk_size, source_size - offset)
    with FileChunkIO(source_path, 'r', offset=offset, bytes=bytes) as fp:
        # 直接调用 bucket 的 upload_part_from_file 方法上传分片
        response = b.upload_part_from_file(
            key_name=key_name,
            upload_id=upload_id,
            fp=fp,
            part_num=i + 1
        )
        print('上传分片 %d, 状态码: %s' % (i + 1, response.status))

# 列出指定上传任务中所有已上传的分片信息
# part_number_marker 指定应该从哪个分片开始列举,只有比设定值大的分片才会被列举
result = b.list_parts(
    bucket_name=b.name,
    key_name=key_name,
    upload_id=upload_id,
    part_number_marker=0
)
print('已上传的分片:')
for part in result.data:
    print('part_number:%s, size:%s, etag:%s' % (part.part_number, part.size, part.etag))

# 方式一：传统方式 - 手动生成完成上传的 XML 请求体
xml_body = '<CompleteMultipartUpload>\n'
for part in result.data:
    xml_body += '  <Part>\n'
    xml_body += '    <PartNumber>%d</PartNumber>\n' % part.part_number
    xml_body += '    <ETag>%s</ETag>\n' % part.etag
    xml_body += '  </Part>\n'
xml_body += '</CompleteMultipartUpload>'

# 完成分片上传（使用 xml_body 参数）
ret = b.complete_multipart_upload(key_name, upload_id, xml_body=xml_body)
if ret and ret.status == 200:
    print("上传成功（方式一）")

# 方式二：新方式 - 直接传入 Part 列表
# 注意：实际使用时，每次只能完成一次上传。以下代码仅作为示例展示不同调用方式
# 可以直接传入 list_parts 返回的 IterableResponseResult
# ret2 = b.complete_multipart_upload(key_name, upload_id, parts=result)
# if ret2 and ret2.status == 200:
#     print("上传成功（方式二 - 使用 IterableResponseResult）")

# 方式三：使用 Part 对象列表
# parts_list = result.data  # 这是 Part 对象列表
# ret3 = b.complete_multipart_upload(key_name, upload_id, parts=parts_list)
# if ret3 and ret3.status == 200:
#     print("上传成功（方式三 - 使用 Part 对象列表）")

# 方式四：使用字典列表
# parts_dict = [
#     {'part_number': 1, 'etag': '"xxx"'},
#     {'part_number': 2, 'etag': '"yyy"'},
# ]
# ret4 = b.complete_multipart_upload(key_name, upload_id, parts=parts_dict)
# if ret4 and ret4.status == 200:
#     print("上传成功（方式四 - 使用字典列表）")

# 列举分片上传事件
# 列举 Bucket 中所有的分片上传事件
key_marker = '<yourKey>'
for p in b.list_multipart_uploads(key_marker=key_marker):
    print('uploadId:%s, key:%s' % (p.id, p.key_name))
    # 列出每个上传任务的分片
    parts_result = b.list_parts(
        bucket_name=b.name,
        key_name=p.key_name,
        upload_id=p.id
    )
    for part in parts_result.data:
        print('  part_number:%s, size:%s, etag:%s, last_modified:%s' % 
              (part.part_number, part.size, part.etag, part.last_modified))

# 取消指定upload_id的分片上传事件,已上传的分片会被删除
# 示例:取消某个上传任务
# response = b.cancel_multipart_upload(key_name, upload_id)
# print('取消上传响应:', response.status)