全部产品
对象存储 OSS

上传文件

更新时间:2017-06-07 13:26:11   分享:   

OSS有多种上传方式,不同的上传方式能够上传的数据大小也不一样。普通上传(PutObject)、追加上传(AppendObject)最多只能上传小于或等于5GB的文件;而分片上传每个分片可以达到5GB,合并后的文件能够达到48.8TB。

首先介绍普通上传,我们会详细展示提供数据的各种方式,即方法中的 data 参数。其他上传接口有类似的data参数,不再赘述。

普通上传

通过 Bucket.put_object 方法,可以上传一个普通文件。

上传字符串

上传内存中的字符串:

  1. # -*- coding: utf-8 -*-
  2. import oss2
  3. auth = oss2.Auth('您的AccessKeyId', '您的AccessKeySecret')
  4. bucket = oss2.Bucket(auth, '您的Endpoint', '您的Bucket名')
  5. bucket.put_object('remote.txt', 'content of object')

也可以指定上传的是bytes:

  1. bucket.put_object('remote.txt', b'content of object')

或是指定为unicode:

  1. bucket.put_object('remote.txt', u'content of object')

事实上,oss2.Bucket.put_object的第二个参数(参数名为data)可以接受两种类型的字符串:

  • bytes:直接上传
  • unicode:会自动转换为UTF-8编码的bytes进行上传

上传本地文件

  1. with open('local.txt', 'rb') as fileobj:
  2. bucket.put_object('remote.txt', fileobj)

对比上传字符串的代码,注意到数据参数既可以是字符串,也可以是这里的文件对象(file object)。

注意:

  • 必须以二进制的方式打开文件,因为内部实现需要知道文件包含的字节数。

Python SDK还提供了一个便捷的方法完成上面的工作:

  1. bucket.put_object_from_file('remote.txt', 'local.txt')

上传网络流

  1. import requests
  2. input = requests.get('http://www.aliyun.com')
  3. bucket.put_object('aliyun.txt', input)

requests.get返回的是一个可迭代对象(iterable),此时Python SDK会通过Chunked Encoding方式上传。

返回值

  1. result = bucket.put_object('remote.txt', 'content of object')
  2. print('http status: {0}'.format(result.status))
  3. print('request_id: {0}'.format(result.request_id))
  4. print('ETag: {0}'.format(result.etag))
  5. print('date: {0}'.format(result.headers['date']))

每个OSS服务器返回的响应都有共同属性:

  • status:HTTP返回码
  • request_id:请求ID
  • headers:HTTP响应头部

etag则是put_object返回值特有的属性。

注意

  • 请求ID唯一标识了一次请求,强烈建议把它作为程序日志的一部分

小结

从上面的示例可以发现,Python SDK上传方法可以接受多种类型的输入源,这主要得益于第三方的requests库。小结一下,输入数据(data参数)可以有如下几种类型:

  • bytes字符串
  • unicode字符串:自动转换为UTF-8编码的bytes进行上传
  • 文件对象(file object):必须以二进制方式打开(如”rb”模式)
  • 可迭代对象(iterable):以Chunked Encoding的方式上传

注意:

  • 对于文件对象,如果是可以seek和tell的,那么会从文件当前位置开始上传,直到文件结束。

断点续传

当需要上传的本地文件很大,或网络状况不够理想,往往会出现上传到中途就失败了。此时,如果对已经上传的数据重新上传,既浪费时间,又占用了网络资源。Python SDK提供了一个易用性接口 oss2.resumable_upload ,用于断点续传本地文件:

  1. oss2.resumable_upload(bucket, 'remote.txt', 'local.txt')

其内部实现是当文件长度大于或等于可选参数 multipart_threshold 时,就进行分片上传。此时,会在HOME目录下建立.py-oss-upload目录,并把当前进度保存在其下的某个文件中。用户也可以通过可选参数store来指定保存进度的目录。

下面是一个完全定制化的例子:

  1. oss2.resumable_upload(bucket, 'remote.txt', 'local.txt',
  2. store=oss2.ResumableStore(root='/tmp'),
  3. multipart_threshold=100*1024,
  4. part_size=100*1024,
  5. num_threads=4)

含义是

  • ResumableStore 指定把进度保存到 /tmp/.py-oss-upload 目录下
  • multipart_threshold 指明只要文件长度不小于100KB就进行分片上传
  • part_size 参数建议每片大小为100KB。如果文件太大,那么分片大小也可能会大于100KB
  • num_threads 参数指定并发上传线程数为4

注意:

  • 请把 oss2.defaults.connection_pool_size 设成大于或等于线程数。
  • 要求 2.1.0 及以后版本

分片上传

采用分片上传,用户可以对上传做更为精细的控制。这适用于诸如预先不知道文件大小、并发上传、自定义断点续传等场景。一次分片上传可以分为三个步骤:

  • 初始化(Bucket.init_multipart_upload):获得Upload ID
  • 上传分片(Bucket.upload_part):这一步可以并发进行
  • 完成上传(Bucket.complete_multipart_upload):合并分片,生成OSS文件

具体例子如下:

  1. import os
  2. from oss2 import SizedFileAdapter, determine_part_size
  3. from oss2.models import PartInfo
  4. key = 'remote.txt'
  5. filename = 'local.txt'
  6. total_size = os.path.getsize(filename)
  7. part_size = determine_part_size(total_size, preferred_size=100 * 1024)
  8. # 初始化分片
  9. upload_id = bucket.init_multipart_upload(key).upload_id
  10. parts = []
  11. # 逐个上传分片
  12. with open(filename, 'rb') as fileobj:
  13. part_number = 1
  14. offset = 0
  15. while offset < total_size:
  16. num_to_upload = min(part_size, total_size - offset)
  17. result = bucket.upload_part(key, upload_id, part_number,
  18. SizedFileAdapter(fileobj, num_to_upload))
  19. parts.append(PartInfo(part_number, result.etag))
  20. offset += num_to_upload
  21. part_number += 1
  22. # 完成分片上传
  23. bucket.complete_multipart_upload(key, upload_id, parts)
  24. # 验证一下
  25. with open(filename, 'rb') as fileobj:
  26. assert bucket.get_object(key).read() == fileobj.read()

其中:

  • determine_part_size是一个用来确定分片大小的帮助函数。
  • SizedFileAdapter(fileobj, size)会生成一个新的file object,起始偏移和原先一样,但最多只能读取size大小。

注意:

  • 三个步骤中的对象名(key)必须一致;上传分片和完成上传中的Upload ID必须一致。

追加上传

可以通过Bucket.append_object方法进行追加上传:

  1. result = bucket.append_object('append.txt', 0, 'content of first append')
  2. bucket.append_object('append.txt', result.next_position, 'content of second append')

首次上传的偏移量(position参数)设为0。如果文件已经存在,且

  • 不是可追加文件,则抛出ObjectNotAppendable异常;
  • 是可追加文件,如果传入的偏移和文件当前长度不等,则抛出PositionNotEqualToLength异常。

如果不是首次上传,可以通过Bucket.head_object方法或上次追加返回值的next_position属性,得到偏移参数。

设置HTTP头

上传时,可以通过headers参数设置OSS支持的HTTP头部,如Content-Type:

  1. bucket.put_object('a.json', '{"age": 1}', headers={'Content-Type': 'application/json; charset=utf-8'})

所能支持的HTTP标准头部列表,请参考API文档中PutObject部分。

设置自定义元信息

通过传入x-oss-meta-为前缀的HTTP头部,可以为文件设置自定义元信息:

  1. bucket.put_object('story.txt', 'a novel', headers={'x-oss-meta-author': 'O. Henry'})

进度条

上传接口都提供了可选参数progress_callback,用来帮助实现进度条功能。下面的代码以Bucket.put_object为例,实现了一个简单的命令行下的进度显示功能(新开一个Python源文件):

  1. # -*- coding: utf-8 -*-
  2. from __future__ import print_function
  3. import os, sys
  4. import oss2
  5. auth = oss2.Auth('您的AccessKeyId', '您的AccessKeySecret')
  6. bucket = oss2.Bucket(auth, '您的Endpoint', '您的Bucket名')
  7. def percentage(consumed_bytes, total_bytes):
  8. if total_bytes:
  9. rate = int(100 * (float(consumed_bytes) / float(total_bytes)))
  10. print('\r{0}% '.format(rate), end='')
  11. sys.stdout.flush()
  12. bucket.put_object('story.txt', 'a'*1024*1024, progress_callback=percentage)

注意:

  • 当无法确定待上传的数据长度时,progress_callback的第二个参数(total_bytes)为None。
  • 进度条的完整示例代码请参看 GitHub

上传回调

put_object、put_object_from_file、complete_multipart_upload提供提供了上传回调功能。下面的代码以Bucket.put_object为例,实现了一个简单的上传回调功能:

  1. # -*- coding: utf-8 -*-
  2. import json
  3. import base64
  4. import os
  5. import oss2
  6. auth = oss2.Auth('您的AccessKeyId', '您的AccessKeySecret')
  7. bucket = oss2.Bucket(auth, '您的Endpoint', '您的Bucket名')
  8. # 准备回调参数
  9. callback_dict = {}
  10. callback_dict['callbackUrl'] = 'http://oss-demo.aliyuncs.com:23450'
  11. callback_dict['callbackHost'] = 'oss-cn-hangzhou.aliyuncs.com'
  12. callback_dict['callbackBody'] = 'filename=${object}&size=${size}&mimeType=${mimeType}'
  13. callback_dict['callbackBodyType'] = 'application/x-www-form-urlencoded'
  14. # 回调参数是json格式,并且需要base64编码
  15. callback_param = json.dumps(callback_dict).strip()
  16. base64_callback_body = base64.b64encode(callback_param)
  17. # 回调参数编码后放在header中传给oss
  18. headers = {'x-oss-callback': base64_callback_body}
  19. # 上传并回调
  20. result = bucket.put_object('story.txt', 'a'*1024*1024, headers)

提示:

  • 上传回调的详细说明请参看 上传回调
  • 上传回调的完整示例代码请参看 GitHub

了解更多

管理文件:罗列、删除文件;查看、更改文件HTTP头、自定义元信息等。

本文导读目录
本文导读目录
以上内容是否对您有帮助?