对象存储BOS

    Object管理

    上传Object

    最简单的上传

    • 基本流程

      1. 创建BOSClient类的实例。
      2. 调用BOSClient putObject方法,可以通过如下二种方式上传Object:文件、二进制数据的形式。
      3. 对返回的BOSPutObjectResponse类型实例,可以执行获取eTag操作。
    • 示例代码

      BOSObjectContent* content = [[BOSObjectContent alloc] init];
      // 以文件方式
      content.objectData.file = @"<file path>";
      
      // 或者以二进制数据方式
      NSData* data = [[NSData alloc] init];
      content.objectData.data = data;
      
      BOSPutObjectRequest* request = [[BOSPutObjectRequest alloc] init];
      request.bucket = @"<bucketname>";
      request.key = @"<objectname>";
      request.objectContent = content;
      
      __block BOSPutObjectResponse* response = nil;
      BCETask* task = [client putObject:request];
      task.then(^(BCEOutput* output) {
        if (output.progress) {
            NSLog(@"put object progress is %@", output.progress);
        }
      
        if (output.response) {
            response = (BOSPutObjectResponse*)output.response;
            NSLog(@"put object success!");
        }
      
        if (output.error) {
            NSLog(@"put object failure");
        }
      });
      [task waitUtilFinished];

      说明:Object以文件的形式上传到BOS中,putObject函数支持不超过5GB的Object上传。在putObject请求处理成功后,BOS会在Header中返回Object的ETag作为文件标识。

    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSObjectContent* content = [[BOSObjectContent alloc] init];
          // 以文件方式
          content.objectData.file = @"<file path>";
      
          // 或者以二进制数据方式
          NSData* data = [[NSData alloc] init];
          content.objectData.data = data;
      
          BOSPutObjectRequest* request = [[BOSPutObjectRequest alloc] init];
          request.bucket = @"<bucketname>";
          request.key = @"<objectname>";
          request.objectContent = content;
      
          __block BOSPutObjectResponse* response = nil;
          BCETask* task = [client putObject:request];
          task.then(^(BCEOutput* output) {
              if (output.progress) {
                  NSLog(@"put object progress is %@", output.progress);
              }
      
              if (output.response) {
                  response = (BOSPutObjectResponse*)output.response;
                  NSLog(@"put object success!");
              }
      
              if (output.error) {
                  NSLog(@"put object failure");
              }
          });
          [task waitUtilFinished];
      }

    设定Object的Http Header

    BOS支持您在上传object时设定Http Header。

    • 基本流程

      1. 创建BOSObjectMetadata类的实例。
      2. 设定BOSObjectMetadata实例的contentEncoding/contentType/contentDisposition等字段。
      3. 将BOSObjectMetadata实例设置到BOSPutObjectRequest的objectContent.metadata字段上。
    • 示例代码

      BOSObjectMetadata* metadata = [[BOSObjectMetadata alloc] init];
      metadata.contentEncoding = @"<encoding>";
      metadata.contentDisposition = @"<content disposition>";
      content.metadata = metadata;
    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSObjectContent* content = [[BOSObjectContent alloc] init];
          // 以文件方式
          content.objectData.file = @"<file path>";
      
          // 或者以二进制数据方式
          NSData* data = [[NSData alloc] init];
          content.objectData.data = data;
      
          BOSObjectMetadata* metadata = [[BOSObjectMetadata alloc] init];
          metadata.contentEncoding = @"<encoding>";
          metadata.contentDisposition = @"<content disposition>";
          content.metadata = metadata;
      
          BOSPutObjectRequest* request = [[BOSPutObjectRequest alloc] init];
          request.bucket = @"<bucketname>";
          request.key = @"<Objectname>";
          request.objectContent = content;
      
          __block BOSPutObjectResponse* response = nil;
          BCETask* task = [client putObject:request];
          task.then(^(BCEOutput* output) {
              if (output.progress) {
                  NSLog(@"put object progress is %@", output.progress);
              }
      
              if (output.response) {
                  response = (BOSPutObjectResponse*)output.response;
                  NSLog(@"put object success!");
              }
      
              if (output.error) {
                  NSLog(@"put object failure");
              }
          });
          [task waitUtilFinished];
      }

    用户自定义元数据

    BOS支持用户自定义元数据来对Object进行描述。

    • 基本流程

      1. 创建BOSObjectMetadata类的实例。
      2. 将自定义元数据字典设置到BOSObjectMetadata的userMetadata字段。
    • 示例代码

      BOSObjectMetadata* metadata = [[BOSObjectMetadata alloc] init];
      metadata.contentEncoding = @"<encoding>";
      metadata.contentDisposition = @"<content disposition>";
      content.metadata = metadata;
      
      NSDictionary* customMetadata = @{
        @"name" : @"my-data"
      };
      content.metadata.userMetadata = customMetadata;

      说明:在上面代码中,用户自定义了一个名字为”name”,值为”my-data”的元数据。当用户下载此Object的时候,此元数据也可以一并得到。一个Object可以有多个类似的参数,但所有的User Meta总大小不能超过2KB。

    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSObjectContent* content = [[BOSObjectContent alloc] init];
          // 以文件方式
          content.objectData.file = @"<file path>";
      
          // 或者以二进制数据方式
          NSData* data = [[NSData alloc] init];
          content.objectData.data = data;
      
          BOSObjectMetadata* metadata = [[BOSObjectMetadata alloc] init];
          metadata.contentEncoding = @"<encoding>";
          metadata.contentDisposition = @"<content disposition>";
          content.metadata = metadata;
      
          NSDictionary* customMetadata = @{
              @"name" : @"my-data"
          };
          content.metadata.userMetadata = customMetadata;
      
          BOSPutObjectRequest* request = [[BOSPutObjectRequest alloc] init];
          request.bucket = @"<bucketname>";
          request.key = @"<objectname>";
          request.objectContent = content;
      
          __block BOSPutObjectResponse* response = nil;
          BCETask* task = [client putObject:request];
          task.then(^(BCEOutput* output) {
              if (output.progress) {
                  NSLog(@"put object progress is %@", output.progress);
              }
      
              if (output.response) {
                  response = (BOSPutObjectResponse*)output.response;
                  NSLog(@"put object success!");
              }
      
              if (output.error) {
                  NSLog(@"put object failure");
              }
          });
          [task waitUtilFinished];
      }

    查看Bucket中的Object

    简单查询

    查看Bucket中Object列表。

    • 基本流程

      1. 创建BOSClient类的实例。
      2. 执行BOSClient listObjects方法,会返回BOSListObjectsResponse类的实例。
      3. 可以对BOSListObjectsResponse类型的contents字段进行枚举,获取key/lastModified/eTag/size/owner。
    • 示例代码

      BOSListObjectsRequest* listObjRequest = [[BOSListObjectsRequest alloc] init];
      listObjRequest.bucket = @"<bucketname>";
      
      __block BOSListObjectsResponse* listObjResponse = nil;
      BCETask* task = [client listObjects:listObjRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            listObjResponse = (BOSListObjectsResponse*)output.response;
      
            for (BOSObjectInfo* object in listObjResponse.contents) {
                NSLog(@"the object key is %@", object.key);
                NSLog(@"the object lastModified is %@", object.lastModified);
                NSLog(@"the object eTag is %@", object.eTag);
                NSLog(@"the object size is %llu", object.size);
                NSLog(@"the object owner id is %@", object.owner.ownerID);
            }
        }
      
        if (output.error) {
            NSLog(@"list objects failure");
        }
      });
      [task waitUtilFinished];

      说明: listObjects方法返回BOSListObjectsResponse对象,BOSListObjectsResponse对象包含了此次listObject请求的返回结果。用户可以通过BOSListObjectsResponse中的contents属性获取所有Object的描述信息。

      • 默认情况下,如果Bucket中的Object数量大于1000,则只会返回1000个Object,并且返回结果中isTruncated值为YES,并返回nextMarker做为下次读取的起点。
      • 若想获取更多的Object,可以使用marker参数分次读取,请参考扩展查询
    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSListObjectsRequest* listObjRequest = [[BOSListObjectsRequest alloc] init];
          listObjRequest.bucket = @"<bucketname>";
      
          __block BOSListObjectsResponse* listObjResponse = nil;
          BCETask* task = [client listObjects:listObjRequest];
          task.then(^(BCEOutput* output) {
              if (output.response) {
                  listObjResponse = (BOSListObjectsResponse*)output.response;
      
                  for (BOSObjectInfo* object in listObjResponse.contents) {
                      NSLog(@"the object key is %@", object.key);
                      NSLog(@"the object lastModified is %@", object.lastModified);
                      NSLog(@"the object eTag is %@", object.eTag);
                      NSLog(@"the object size is %llu", object.size);
                      NSLog(@"the object owner id is %@", object.owner.ownerID);
                  }
              }
      
              if (output.error) {
                  NSLog(@"list objects failure");
              }
          });
          [task waitUtilFinished];
      }

    扩展查询

    用户可以通过设置BOSListObjectsResquest参数来完成更多扩展查询操作设置。BOSListObjectsResquest中可以设置的扩展参数如下:

    参数名称 说明 默认值
    maxKeys 设定此次返回Object的最大个数,不可超过1000。 1000
    prefix 设定objectKey的前缀,前缀是指objectKey包含并以prefix的值作为开始。通常与delimiter配合在查询模拟文件夹中使用。 -
    delimiter 是一个分隔符,用来对objectKey进行分层。通常与prefix配合在查询模拟文件夹中使用。从prefix开始到第一次出现delimiter字符之间的objectKey称为:commonPrefixes。 -
    marker 是一个字符串,用来设定返回结果的起始位置。设定marker值之后,返回的Object会从marker值之后按字母排序开始返回。 -
    • 基本流程

      1. 创建BOSListObjectsResquest类的实例。
      2. 设置BOSListObjectsResquest的delimiter/marker/prefix/maxKeys等字段,实现更多的扩展查询操作。
      3. 创建BOSClient类的实例,执行listObjects。
    • 示例代码

      BOSListObjectsRequest* listObjRequest = [[BOSListObjectsRequest alloc] init];
      listObjRequest.bucket = @"<bucketname>";
      listObjRequest.marker = @"<marker>";
      
      __block BOSListObjectsResponse* listObjResponse = nil;
      BCETask* task = [client listObjects:listObjRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            listObjResponse = (BOSListObjectsResponse*)output.response;
      
            for (BOSObjectInfo* object in listObjResponse.contents) {
                NSLog(@"the object key is %@", object.key);
                NSLog(@"the object lastModified is %@", object.lastModified);
                NSLog(@"the object eTag is %@", object.eTag);
                NSLog(@"the object size is %llu", object.size);
                NSLog(@"the object owner id is %@", object.owner.ownerID);
            }
        }
      
        if (output.error) {
            NSLog(@"list objects failure");
        }
      });
      [task waitUtilFinished];
    • 完整示例

      示例一:

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSListObjectsRequest* listObjRequest = [[BOSListObjectsRequest alloc] init];
          listObjRequest.bucket = @"<bucketname>";
          listObjRequest.marker = @"<marker>";
      
          __block BOSListObjectsResponse* listObjResponse = nil;
          BCETask* task = [client listObjects:listObjRequest];
          task.then(^(BCEOutput* output) {
              if (output.response) {
                  listObjResponse = (BOSListObjectsResponse*)output.response;
      
                  for (BOSObjectInfo* object in listObjResponse.contents) {
                      NSLog(@"the object key is %@", object.key);
                      NSLog(@"the object lastModified is %@", object.lastModified);
                      NSLog(@"the object eTag is %@", object.eTag);
                      NSLog(@"the object size is %llu", object.size);
                      NSLog(@"the object owner id is %@", object.owner.ownerID);
                  }
              }
      
              if (output.error) {
                  NSLog(@"list objects failure");
              }
          });
          [task waitUtilFinished];
      }

      示例二:使用nextMarker的完整示例。

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSListObjectsRequest* listObjRequest = [[BOSListObjectsRequest alloc] init];
          listObjRequest.bucket = @"<bucketname>";
          listObjRequest.marker = @"<marker>";
      
          __block BOSListObjectsResponse* listObjResponse = nil;
          BCETask* task = [client listObjects:listObjRequest];
          task.then(^(BCEOutput* output) {
              if (output.response) {
                  listObjResponse = (BOSListObjectsResponse*)output.response;
              }
      
              if (output.error) {
                  NSLog(@"list objects failure");
              }
          });
          [task waitUtilFinished];
      
          if (listObjResponse != nil) {
              listObjRequest.marker = listObjResponse.nextMarker;
              task = [client listObjects:listObjRequest];
              task.then(^(BCEOutput* output) {
                  if (output.response) {
                      listObjResponse = (BOSListObjectsResponse*)output.response;
      
                      for (BOSObjectInfo* object in listObjResponse.contents) {
                          NSLog(@"the object key is %@", object.key);
                          NSLog(@"the object lastModified is %@", object.lastModified);
                          NSLog(@"the object eTag is %@", object.eTag);
                          NSLog(@"the object size is %llu", object.size);
                          NSLog(@"the object owner id is %@", object.owner.ownerID);
                      }
                  }
              });
          }
      }

    查询模拟文件夹

    由于BOS本身是一个(<Key>,<Value>)的存储系统,所以原则上并不会存在“文件夹”的概念,但您可以通过delimiterprefix参数的配合进行文件夹功能模拟。

    假设Bucket中有5个文件:bos.jpg,fun/,fun/test.jpg,fun/movie/001.avi,fun/movie/007.avi,可以把 “/” 符号作为分隔符模拟文件夹。

    递归列出模拟文件夹下所有文件

    可以通过设置prefix参数来获取某个模拟文件夹下所有的文件:

    BOSListObjectsRequest* listObjRequest = [[BOSListObjectsRequest alloc] init];
    listObjRequest.bucket = @"<bucketname>";
     listObjRequest.prefix = @"fun/";
    
    __block BOSListObjectsResponse* listObjResponse = nil;
    BCETask* task = [client listObjects:listObjRequest];
    task.then(^(BCEOutput* output) {
        if (output.response) {
            listObjResponse = (BOSListObjectsResponse*)output.response;
    
            for (BOSObjectInfo* object in listObjResponse.contents) {
                NSLog(@"%@", object.key);
            }
        }
    });
    [task waitUtilFinished];

    输出:

    Objects:
    fun/
    fun/movie/001.avi
    fun/movie/007.avi
    fun/test.jpg

    查看模拟文件夹下的文件和子文件夹

    prefixdelimiter结合的情况下,可以列出模拟文件夹下的文件和子文件夹:

    BOSListObjectsRequest* listObjRequest = [[BOSListObjectsRequest alloc] init];
    listObjRequest.bucket = @"<bucketname>";
    
    // 指定"/"为模拟文件夹的分隔符
    listObjRequest.delimiter = @"/";
    
    // 列出fun文件夹下的所有文件和子文件夹
    listObjRequest.prefix = @"fun/";
    
    __block BOSListObjectsResponse* listObjResponse = nil;
    BCETask* task = [client listObjects:listObjRequest];
    task.then(^(BCEOutput* output) {
        if (output.response) {
            listObjResponse = (BOSListObjectsResponse*)output.response;
        }
    
        if (output.error) {
            NSLog(@"list objects failure");
        }
    });
    [task waitUtilFinished];
    
    // 遍历所有Object
    NSLog(@"Objects:");
    for (BOSObjectInfo* object in listObjResponse.contents) {
        NSLog(@"%@", object.key);
    }
    
    // 遍历所有CommonPrefix
    NSLog(@"CommonPrefixs:");
    for (NSString* commonPrefix in listObjResponse.commonPrefixes) {
        NSLog(@"%@", commonPrefix);
    }

    输出:

    Objects:
    fun/
    fun/test.jpg
    
    CommonPrefixs:
    fun/movie/

    说明: 返回的结果中,contents的列表中给出的是fun文件夹下的文件。而commonPrefixs的列表中给出的是fun文件夹下的所有子文件夹。可以看出fun/movie/001.avifun/movie/007.avi两个文件并没有被列出来,因为它们属于fun文件夹下的movie子文件夹下的文件。

    获取Object

    简单的获取Object

    用户可以通过如下代码将Object读取到内存中。

    • 基本流程

      1. 创建BOSClient类的实例。
      2. 执行BOSClient getObject方法,会返回BOSGetObjectResponse实例。
      3. 访问BOSGetObjectResponse示例的objectContent.objectData.data属性,获取数据。
    • 示例代码

      __block BOSGetObjectResponse* getObjResponse = nil;
      BOSGetObjectRequest* getObjRequest = [[BOSGetObjectRequest alloc] init];
      getObjRequest.bucket = @"<bucketname>";
      getObjRequest.key = @"<objectname>";
      BCETask* task = [client getObject:getObjRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            getObjResponse = (BOSGetObjectResponse*)output.response;
            NSLog(@"get object success!");
        }
      
        if (output.error) {
            NSLog(@"get object failure with %@", outpu****t.error);
        }
      
        if (output.progress) {
            NSLog(@"the get object progress is %@", output.progress);
        }
      });
      [task waitUtilFinished];
      
      // 获取内存中的数据
      NSData* data = getObjResponse.objectContent.objectData.data;

      注意:

      • BOSObjectContent中包含了Object的各种信息,包含Object所在的Bucket、Object的名称、MetaData以及数据存储。
      • BOSObjectMetadata中包含了Object上传时定义的ETag,Http Header以及自定义的元数据。
      • 通过BOSObjectContent的objectData属性,获取到Object的数据。
    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          __block BOSGetObjectResponse* getObjResponse = nil;
          BOSGetObjectRequest* getObjRequest = [[BOSGetObjectRequest alloc] init];
          getObjRequest.bucket = @"<bucketname>";
          getObjRequest.key = @"<objectname>";
          BCETask* task = [client getObject:getObjRequest];
          task.then(^(BCEOutput* output) {
              if (output.response) {
                  getObjResponse = (BOSGetObjectResponse*)output.response;
                  NSLog(@"get object success!");
              }
      
              if (output.error) {
                  NSLog(@"get object failure with %@", output.error);
              }
      
              if (output.progress) {
                  NSLog(@"the get object progress is %@", output.progress);
              }
          });
          [task waitUtilFinished];
      
          NSData* data = getObjResponse.objectContent.objectData.data;
      }

    下载Object的一部分内容

    • 基本流程

      1. 创建BOSGetObjectRequest类的实例。
      2. BOSGetObjectRequestt实例的rangeStart和/或rangeEnd字段。
      3. 执行client getObject操作。
    • 示例代码

      __block BOSGetObjectResponse* getObjResponse = nil;
      BOSGetObjectRequest* getObjRequest = [[BOSGetObjectRequest alloc] init];
      getObjRequest.bucket = @"<bucketname>";
      getObjRequest.key = @"<objectname>";
      
      // 获取前100个字节
      getObjRequest.rangeStart = @"0";
      getObjRequest.rangeStart = @"99";
      
      BCETask* task = [client getObject:getObjRequest];
      task.then(^(BCEOutput* output) {
          if (output.response) {
              getObjResponse = (BOSGetObjectResponse*)output.response;
              NSLog(@"get object success!");
          }
      
          if (output.error) {
              NSLog(@"get object failure with %@", output.error);
          }
      
          if (output.progress) {
              NSLog(@"the get object progress is %@", output.progress);
          }
      });
      [task waitUtilFinished];
      
      NSData* data = getObjResponse.objectContent.objectData.data;

      说明:用户可以用此功能实现文件的分段下载和断点续传。

    下载Object到指定路径

    用户可以通过如下代码直接将Object下载到指定路径。

    • 基本流程

      1. 创建BOSGetObjectRequest类的实例。
      2. 设置要保存的文件名到BOSGetObjectRequest实例的file字段。
      3. 执行client getObject操作。
      4. Object可以直接下载到指定路径。
    • 示例代码

      __block BOSGetObjectResponse* getObjResponse = nil;
      BOSGetObjectRequest* getObjRequest = [[BOSGetObjectRequest alloc] init];
      getObjRequest.bucket = @"<bucketname>";
      getObjRequest.key = @"<objectname>";
      
      // 设置保存到的文件路径
      getObjRequest.file = @"<file>";
      
      BCETask* task = [client getObject:getObjRequest];
      task.then(^(BCEOutput* output) {
          if (output.response) {
              getObjResponse = (BOSGetObjectResponse*)output.response;
              NSLog(@"get object success!");
          }
      
          if (output.error) {
              NSLog(@"get object failure with %@", output.error);
          }
      
          if (output.progress) {
              NSLog(@"the get object progress is %@", output.progress);
          }
      });
      [task waitUtilFinished];

    获取Object的storageClass

    Object的storage class属性分为STANDARD(标准存储), STANDARD_IA(低频存储)和COLD(冷存储)。

    示例代码

    __block BOSGetObjectMetadataResponse* getObjMetaResponse = nil;
    BCETask* task = [client getObjectMetadata:@"<bucketname>" objectKey:@"<objectname>"];
    task.then(^(BCEOutput* output) {
        if (output.response) {
            getObjMetaResponse = (BOSGetObjectMetadataResponse*)output.response;
            NSString storageClass = getObjMetaResponse.storageClass;
            NSLog(@"get object storageClass success!");
        }
    
        if (output.error) {
            NSLog(@"get object storageClass failure");
        }
    });
    [task waitUtilFinished];

    只获取ObjectMetadata

    通过getObjectMetadata 方法可以只获取 Object metadata 而不获取Object的实体。

    • 示例代码

      __block BOSGetObjectMetadataResponse* getObjMetaResponse = nil;
      BCETask* task = [client getObjectMetadata:@"<bucketname>" objectKey:@"<objectname>"];
      task.then(^(BCEOutput* output) {
          if (output.response) {
              getObjMetaResponse = (BOSGetObjectMetadataResponse*)output.response;
              NSLog(@"get object metadata success!");
          }
      
          if (output.error) {
              NSLog(@"get object metadata failure");
          }
      });
      [task waitUtilFinished];

    获取Object的URL

    您可以通过如下代码获取指定Object的URL,该功能通常用于您将Object的URL临时分享给其他用户的场景。

    • 基本流程

      1. 创建BOSClient类的实例。
      2. 执行BOSClient generatePresignedUrl方法。
      3. 返回一个Object的URL。
    • 示例代码

      __block BOSGeneratePresignedUrlResponse *generateObjetUrlRes = nil;
        BCEOutput_ output = [client generatePresignedUrl:@"<bucketname>" objectKey:@"<objectname>" expirationInSeconds:<ExpirationInSeconds>];
        if (output.response) {
            generateObjetUrlRes = (BOSGeneratePresignedUrlResponse_)output.response;
            NSLog(@"get url success, the usrlstting is : %@", [generateObjetUrlRes.objectUrl absoluteString]);
        }
        if (output.error) {
            NSLog(@"get url failure, error : %@:", output.error);
        }

    说明: ExpirationInSeconds为指定的URL有效时长,时间从当前时间算起,为可选参数,不配置时系统默认值为1800秒。如果要设置为永久不失效的时间,可以将ExpirationInSeconds参数设置为 -1,不可设置为其他负数。

    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          __block BOSGeneratePresignedUrlResponse *generateObjetUrlRes = nil;
            BCEOutput* output = [client generatePresignedUrl:@"<bucketname>" objectKey:@"<objectname>" expirationInSeconds:<ExpirationInSeconds>];
          if (output.response) {
          generateObjetUrlRes = 	(BOSGeneratePresignedUrlResponse*)output.response;
          NSLog(@"get url success, the usrlstting is : %@", [generateObjetUrlRes.objectUrl absoluteString]);
          }
          if (output.error) {
          NSLog(@"get url failure, error : %@:", output.error);
          }
      }

    删除Object

    • 基本流程

      1. 创建BOSClient类的实例。
      2. 执行BOSClient deleteObject方法。
      3. 若操作失败后产生错误。
    • 示例代码

      __block BOSDeleteObjectResponse* response = nil;
      BCETask* task = [client deleteObject:@"<bucketname>" objectKey:@"<objectname>"];
      task.then(^(BCEOutput* output) {
          if (output.response) {
              response = (BOSDeleteObjectResponse*)output.response;
              NSLog(@"delete obj success!");
          }
      
          if (output.error) {
              NSLog(@"delete obj failure");
          }
      });
      [task waitUtilFinished];
    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          __block BOSDeleteObjectResponse* response = nil;
          BCETask* task = [client deleteObject:@"<bucketname>" objectKey:@"<objectname>"];
          task.then(^(BCEOutput* output) {
            if (output.response) {
                response = (BOSDeleteObjectResponse*)output.response;
                NSLog(@"delete obj success!");
            }
      
            if (output.error) {
                NSLog(@"delete obj failure");
            }
          });
          [task waitUtilFinished];
      }

    拷贝Object

    简单拷贝Object

    • 基本流程

      1. 创建BOSClient类的实例。
      2. 执行BOSClient copyObject方法。
      3. 返回BOSCopyObjectResponse类实例,可通过eTag/lastModified等属性获取eTag和最后修改时间。
    • 示例代码

      BOSCopyObjectRequest* request = [[BOSCopyObjectRequest alloc] init];
      request.bucket = @"<bucketname>";
      request.key = @"<objectname>";
      request.source = @"<sourceBucket>/<sourceObject";
      
      __block BOSCopyObjectResponse* response = nil;
      BCETask* task = [client copyObject:request];
      task.then(^(BCEOutput* output) {
          if (output.response) {
              response = (BOSCopyObjectResponse*)output.response;
              NSLog(@"copy obj success!");
          }
      
          if (output.error) {
              NSLog(@"copy obj failure");
          }
      });
      [task waitUtilFinished];
    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSCopyObjectRequest* request = [[BOSCopyObjectRequest alloc] init];
          request.bucket = @"<bucketname>";
          request.key = @"<objectname>";
          request.source = @"<sourceBucket>/<sourceObject";
      
          __block BOSCopyObjectResponse* response = nil;
          BCETask* task = [client copyObject:request];
          task.then(^(BCEOutput* output) {
            if (output.response) {
                response = (BOSCopyObjectResponse*)output.response;
                NSLog(@"copy obj success!");
            }
      
            if (output.error) {
                NSLog(@"copy obj failure");
            }
          });
          [task waitUtilFinished];
      }

      说明:copyObject 方法返回一个 BOSCopyObjectResponse 对象,该对象中包含了新Object的ETag和修改时间。

    指定条件拷贝Object

    您也可以通过指定条件来实现Object的拷贝。该功能一般用于如下场景:

    • Copy一个Object但重新设置meta。
    • 重置某个现有Object的meta(把源和目标设置为同一个Object)。
    • 当源Object的eTag与指定的eTag相同时复制;
    • 当源Object的eTag与指定的eTag不相同时复制;
    • 当源Object的在指定的时间后没有被修改时复制;
    • 当源Object的在指定的时间后被修改过时复制;

    具体内容如下:

    • 基本流程

      1. 创建BOSCopyObjectRequest类的实例,传入<source>,<ifMatchEtag>, <ifNotMatchEtag>,<ifModifiedSince>,<ifUnmodifiedSince>, <metadataDirective>参数。
      2. 返回BOSCopyObjectResponse类实例,可通过eTag/lastModified属性获取eTag和最后修改时间。
    • 示例代码

      BOSCopyObjectRequest* request = [[BOSCopyObjectRequest alloc] init];
      request.bucket = @"<bucketname>";
      request.key = @"<objectname>";
      request.source = @"<sourceBucket>/<sourceObject";
      request.metadataDirective = @"replace";
      request.ifModifiedSince = @"Wed, 01 Mar 2006 12:00:00 GMT";
      
      __block BOSCopyObjectResponse* response = nil;
      BCETask* task = [client copyObject:request];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            response = (BOSCopyObjectResponse*)output.response;
            NSLog(@"copy obj success!");
        }
      
        if (output.error) {
            NSLog(@"copy obj failure");
        }
      });
      [task waitUtilFinished];

      说明:BOSCopyObjectRequest 允许用户修改目的Object的ObjectMeta,同时也提供 MatchingETagConstraints 参数的设定。

    Object的分块上传

    除了通过putObject()方法上传文件到BOS以外,BOS还提供了另外一种上传模式:分块上传(Multipart Upload)。用户可以在如下的应用场景内(但不仅限于此),使用分块上传模式,如:

    • 需要支持断点上传。
    • 上传超过5GB大小的文件。
    • 网络条件较差,和BOS的服务器之间的连接经常断开。
    • 需要流式地上传文件。
    • 上传文件之前,无法确定上传文件的大小。

    分块完成Multipart Upload

    假设有一个文件,本地路径为/path/to/file.zip,由于文件比较大,使用分块上传其传输到BOS中。

    • 基本流程

      1. 初始化Multipart Upload。
      2. 上传分块。
      3. 完成分块上传。

    初始化Multipart Upload

    使用initiateMultipartUpload方法来初始化一个分块上传事件:

    • 示例代码

      BOSInitiateMultipartUploadRequest* initMPRequest = [[BOSInitiateMultipartUploadRequest alloc] init];
      initMPRequest.bucket = @"<bucketname>";
      initMPRequest.key = @"<objectname>";
      initMPRequest.contentType = @"<content type>";
      
      __block BOSInitiateMultipartUploadResponse* initMPResponse = nil;
      BCETask* task = [client initiateMultipartUpload:initMPRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            initMPResponse = (BOSInitiateMultipartUploadResponse*)output.response;
            NSLog(@"initiate multipart upload success!");
        }
      
        if (output.error) {
            NSLog(@"initiate multipart upload failure");
        }
      });
      [task waitUtilFinished];
      
      NSString* uploadID = initMPResponse.uploadId;

      说明:initiateMultipartUpload的返回结果中含有uploadId,它是区分分块上传事件的唯一标识,在后面的操作中,我们将用到它。

    上传分块

    将文件分块上传。

    • 示例代码

      // 计算分块个数
      NSString* file = @"/path/to/file.zip";
      NSDictionary<NSString*, id>* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:file error:nil];
      uint64_t fileSize = attr.fileSize;
      uint64_t partSize = 1024 * 1024 * 5L;
      uint64_t partCount = fileSize / partSize;
      if (fileSize % partSize != 0) {
        ++partCount;
      }
      
      NSMutableArray<BOSPart*>* parts = [NSMutableArray array];
      NSFileHandle* handle = [NSFileHandle fileHandleForReadingAtPath:@"/path/to/file.zip"];
      for (uint64_t i = 0; i < partCount; ++i) {
        // seek
        uint64_t skip = partSize * i;
        [handle seekToFileOffset:skip];
        uint64_t size = (partSize < fileSize - skip) ? partSize : fileSize - skip;
      
        // data
        NSData* data = [handle readDataOfLength:size];
      
        // request
        BOSUploadPartRequest* uploadPartRequest = [[BOSUploadPartRequest alloc] init];
        uploadPartRequest.bucket = @"<bucketname>";
        uploadPartRequest.key = @"<objectname>";
        uploadPartRequest.objectData.data = data;
        uploadPartRequest.partNumber = i + 1;
        uploadPartRequest.uploadId = uploadID;
      
        __block BOSUploadPartResponse* uploadPartResponse = nil;
        task = [client uploadPart:uploadPartRequest];
        task.then(^(BCEOutput* output) {
            if (output.response) {
                uploadPartResponse = (BOSUploadPartResponse*)output.response;
                BOSPart* part = [[BOSPart alloc] init];
                part.partNumber = i + 1;
                part.eTag = uploadPartResponse.eTag;
                        [parts addObject:part];
            }
        });
        [task waitUtilFinished];
      }

      注意:上面代码的核心是调用uploadPart方法来上传每一个分块,但是要注意以下几点:

      • uploadPart方法要求除最后一个Part以外,其他的Part大小都要大于等于5MB。但是Upload Part接口并不会立即校验上传Part的大小;只有当Complete Multipart Upload的时候才会校验。
      • 为了保证数据在网络传输过程中不出现错误,建议您在uploadPart后,使用每个分块BOS返回的Content-MD5值分别验证已上传分块数据的正确性。当所有分块数据合成一个Object后,不再含MD5值。
      • Part号码的范围是1~10000。如果超出这个范围,BOS将返回InvalidArgument的错误码。
      • 每次上传Part时都要把流定位到此次上传块开头所对应的位置。
      • 每次上传Part之后,BOS的返回结果会包含一个BOSPart对象,它是上传块的ETag与块编号(PartNumber)的组合,在后续完成分块上传的步骤中会用到它,因此需要将其保存起来。一般来讲这些BOSPart对象将被保存到数组中。

    完成分块上传

    • 示例代码

      BOSCompleteMultipartUploadRequest* compMultipartRequest = [[BOSCompleteMultipartUploadRequest alloc] init];
      compMultipartRequest.bucket = @"<bucketname>";
      compMultipartRequest.key = @"<objectname>";
      compMultipartRequest.uploadId = uploadID;
      compMultipartRequest.parts = parts;
      
      __block BOSCompleteMultipartUploadResponse* complResponse = nil;
      task = [client completeMultipartUpload:compMultipartRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            complResponse = (BOSCompleteMultipartUploadResponse*)output.response;
            NSLog(@"complte multiparts success!");
        }
      
        if (output.error) {
            NSLog(@"complte multiparts failure %@", output.error);
        }
      });
      [task waitUtilFinished];

      说明:上面代码中的 parts 是第二步中保存的parts的列表,BOS收到用户提交的Part列表后,会逐一验证每个数据Part的有效性。当所有的数据Part验证通过后,BOS将把这些数据part组合成一个完整的Object。

    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
      // 初始化
      BCECredentials* credentials = [[BCECredentials alloc] init];
      credentials.accessKey = @"<access key>";
      credentials.secretKey = @"<secret key>";
      BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
      configuration.credentials = credentials;
      
      BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
      // 初始化分块上传
      BOSInitiateMultipartUploadRequest* initMPRequest = [[BOSInitiateMultipartUploadRequest alloc] init];
      initMPRequest.bucket = @"<bucketname>";
      initMPRequest.key = @"<objectname>";
      initMPRequest.contentType = @"<content type>";
      
       __block BOSInitiateMultipartUploadResponse* initMPResponse = nil;
       BCETask* task = [client initiateMultipartUpload:initMPRequest];
       task.then(^(BCEOutput* output) {
           if (output.response) {
               initMPResponse = (BOSInitiateMultipartUploadResponse*)output.response;
               NSLog(@"initiate multipart upload success!");
           }
      
           if (output.error) {
               NSLog(@"initiate multipart upload failure");
           }
       });
       [task waitUtilFinished];
      
       NSString* uploadID = initMPResponse.uploadId;
      
       // 计算分块个数
       NSString* file = @"/path/to/file.zip";
       NSDictionary<NSString*, id>* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:file error:nil];
       uint64_t fileSize = attr.fileSize;
       uint64_t partSize = 1024 * 1024 * 5L;
       uint64_t partCount = fileSize / partSize;
       if (fileSize % partSize != 0) {
           ++partCount;
       }
      
       NSMutableArray<BOSPart*>* parts = [NSMutableArray array];
      
       NSFileHandle* handle = [NSFileHandle fileHandleForReadingAtPath:@"/path/to/file.zip"];
       for (uint64_t i = 0; i < partCount; ++i) {
           // seek
           uint64_t skip = partSize * i;
           [handle seekToFileOffset:skip];
           uint64_t size = (partSize < fileSize - skip) ? partSize : fileSize - skip;
      
           // data
           NSData* data = [handle readDataOfLength:size];
      
           // request
           BOSUploadPartRequest* uploadPartRequest = [[BOSUploadPartRequest alloc] init];
           uploadPartRequest.bucket = @"<bucketname>";
           uploadPartRequest.key = @"<objectname>";
           uploadPartRequest.objectData.data = data;
           uploadPartRequest.partNumber = i + 1;
           uploadPartRequest.uploadId = uploadID;
      
           __block BOSUploadPartResponse* uploadPartResponse = nil;
           task = [client uploadPart:uploadPartRequest];
           task.then(^(BCEOutput* output) {
               if (output.response) {
                   uploadPartResponse = (BOSUploadPartResponse*)output.response;
                   BOSPart* part = [[BOSPart alloc] init];
                   part.partNumber = i + 1;
                   part.eTag = uploadPartResponse.eTag;
                           [parts addObject:part];
               }
           });
           [task waitUtilFinished];
       }
      
       BOSCompleteMultipartUploadRequest* compMultipartRequest = [[BOSCompleteMultipartUploadRequest alloc] init];
       compMultipartRequest.bucket = @"<bucketname>";
       compMultipartRequest.key = @"<objectname>";
       compMultipartRequest.uploadId = uploadID;
       compMultipartRequest.parts = parts;
      
       __block BOSCompleteMultipartUploadResponse* complResponse = nil;
       task = [client completeMultipartUpload:compMultipartRequest];
       task.then(^(BCEOutput* output) {
           if (output.response) {
               complResponse = (BOSCompleteMultipartUploadResponse*)output.response;
               NSLog(@"complte multiparts success!");
           }
      
           if (output.error) {
               NSLog(@"complte multiparts failure %@", output.error);
           }
       });
       [task waitUtilFinished];
      }

    取消分块上传

    用户可以使用abortMultipartUpload方法取消分块上传。

    • 示例代码

      BOSAbortMultipartUploadRequest* abortRequest = [[BOSAbortMultipartUploadRequest alloc] init];
      abortRequest.bucket = @"bucket";
      abortRequest.key = @"<objectname>";
      abortRequest.uploadId = uploadID;
      
      __block BOSAbortMultipartUploadResponse* abortResponse = nil;
      task = [client abortMultipartUpload:abortRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            abortResponse = (BOSAbortMultipartUploadResponse*)output.response;
            NSLog(@"abort multiparts success!");
        }
      
        if (output.error) {
            NSLog(@"abort multiparts failure %@", output.error);
        }
      });
      [task waitUtilFinished];

    获取未完成的分块上传

    用户可以使用listMultipartUploads方法获取Bucket内未完成的分块上传事件。

    • 基本流程

      1. 创建BOSListMultipartUploadsRequest类的实例,传入<BucketName>参数。
      2. 创建BOSClient类的实例,执行BOSClient listMultipartUploads方法。
      3. listMultipartUploads返回所有未完成的分块上传信息。
    • 示例代码

      BOSListMultipartUploadsRequest* listMultipartRequest = [[BOSListMultipartUploadsRequest alloc] init];
      listMultipartRequest.bucket = @"<bucketname>";
      
      __block BOSListMultipartUploadsResponse* listMultipartResponse = nil;
      task = [client listMultipartUploads:listMultipartRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            listMultipartResponse = (BOSListMultipartUploadsResponse*)output.response;
            NSLog(@"list multipart success");
        }
      
        if (output.error) {
            NSLog(@"list multipart failure %@", output.error);
        }
      });
      [task waitUtilFinished];

      注意:

      • 默认情况下,如果Bucket中的分块上传事件的数目大于1000,则只会返回1000个Object,并且返回结果中IsTruncated的值为True,同时返回nextKeyMarker作为下次读取的起点。
      • 若想获取更多分块上传事件,可以使用keyMarker参数分次读取。
    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSListMultipartUploadsRequest* listMultipartRequest = [[BOSListMultipartUploadsRequest alloc] init];
          listMultipartRequest.bucket = @"<bucketname>";
      
          __block BOSListMultipartUploadsResponse* listMultipartResponse = nil;
          BCETask* task = [client listMultipartUploads:listMultipartRequest];
          task.then(^(BCEOutput* output) {
            if (output.response) {
                listMultipartResponse = (BOSListMultipartUploadsResponse*)output.response;
                NSLog(@"list multipart success");
            }
      
            if (output.error) {
                NSLog(@"list multipart failure %@", output.error);
            }
          });
          [task waitUtilFinished];
      
          for (BOSMultipartUpload* upload in listMultipartResponse.uploads) {
              NSLog(@"upload id : %@", upload.uploadId);
          }
      }

    获取所有已上传的分块信息

    用户可以使用listParts方法获取某个上传事件中所有已上传的块。

    • 基本流程

      1. 创建BOSListPartsRequest类的实例,传入<BucketName>,<ObjectKey>, <UploadId>参数。
      2. 创建BOSClient类的实例,执行BOSClient listParts方法。
      3. listParts返回所有已上传part的信息。
    • 示例代码

      BOSListPartsRequest* listPartsRequest = [[BOSListPartsRequest alloc] init];
      listPartsRequest.bucket = @"<bucketname>";
      listPartsRequest.key = @"<objectname>";
      listPartsRequest.uploadId = @"<upload id>";;
      
      __block BOSListPartsResponse* listPartsResponse = nil;
      BCETask* task = [client listParts:listPartsRequest];
      task.then(^(BCEOutput* output) {
        if (output.response) {
            listPartsResponse = (BOSListPartsResponse*)output.response;
            NSLog(@"list parts success!");
        }
      
        if (output.error) {
            NSLog(@"list part failure %@", output.error);
        }
      });
      [task waitUtilFinished];
      
      for (BOSPart* part in listPartsResponse.parts) {
          NSLog(@"part etag %@", part.eTag);
      }

      注意:

      • 默认情况下,如果Bucket中的分块上传事件的数目大于1000,则只会返回1000个Object,并且返回结果中IsTruncated的值为True,同时返回NextPartNumberMarker作为下次读取的起点。
      • 若想获取更多已上传的分块信息,可以使用PartNumberMarker参数分次读取。
    • 完整示例

      #import <BaiduBCEBasic/BaiduBCEBasic.h>
      #import <BaiduBCEBOS/BaiduBCEBOS.h>
      
      void example(void) {
          // 初始化
          BCECredentials* credentials = [[BCECredentials alloc] init];
          credentials.accessKey = @"<access key>";
          credentials.secretKey = @"<secret key>";
          BOSClientConfiguration* configuration = [[BOSClientConfiguration alloc] init];
          configuration.credentials = credentials;
      
          BOSClient* client = [[BOSClient alloc] initWithConfiguration:configuration];
      
          BOSListPartsRequest* listPartsRequest = [[BOSListPartsRequest alloc] init];
          listPartsRequest.bucket = @"<bucketname>";
          listPartsRequest.key = @"<objectname>";
          listPartsRequest.uploadId = @"<upload id>";;
      
          __block BOSListPartsResponse* listPartsResponse = nil;
          BCETask* task = [client listParts:listPartsRequest];
          task.then(^(BCEOutput* output) {
          if (output.response) {
              listPartsResponse = (BOSListPartsResponse*)output.response;
              NSLog(@"list parts success!");
          }
      
          if (output.error) {
              NSLog(@"list part failure %@", output.error);
          }
          });
          [task waitUtilFinished];
      
          for (BOSPart* part in listPartsResponse.parts) {
              NSLog(@"part etag %@", part.eTag);
          }
      }
    上一篇
    Bucket管理
    下一篇
    日志