++ArchiveManager++是高级接口的入口,包含上传和下载两类接口,每一个接口均包含阻塞和异步两种实现,其中方法名以Async结尾的为异步方法。所有接口均可能抛出两种异常,分别为++OASClientException++++OASServerException++,详见本章异常一节。

背景信息

++ArchiveManager++的可通过如下三种方式获得:

// 使用默认配置
ServiceCredentials credentials = new ServiceCredentials(
    "[yourAccessKeyID]", "[yourAccessKeySecret]");
ArchiveManager archiveManager = OASFactory.archiveManagerFactory(credentials,
    "http://cn-hangzhou.oas.aliyuncs.com");
//初始化认证
ServiceCredentials credentials = new ServiceCredentials(
    "[yourAccessKeyId]", "[yourAccessKeySecret]");
//服务地址
ServiceHost serviceHost = new ServiceHost("http://cn-hangzhou.oas.aliyuncs.com", 80);
//客户端配置
ClientConfiguration clientConfiguration = new ClientConfiguration();
ArchiveManager archiveManager = new ArchiveManager(serviceHost, credentials, clientConf);
// 使用AliyunOASClient初始化
AliyunOASClient aliyunOASClient = OASFactory.aliyunOASClientFactory(
    serviceHost, credentials, clientConfiguration).withLogger();
ArchiveManager archiveManager = OASFactory.archiveManagerFactory(aliyunOASClient);

其中,ClientConfiguration的具体使用请参阅本章最后一节自定义配置,AliyunOASClient的具体介绍参见低级接口。为使示例更简洁,本章示例中若出现未定义的archiveManager变量,均是指ArchiveManager对象。

在Multipart上传、大文件下载时,多线程可以提高执行效率,高级接口提供让用户指定线程并发数量:

// 设置多线程并发数为5,默认:3,最大:10
// 设置单上传链路重试次数,默认为2,最大:3
ArchiveManager manager = OASFactory.archiveManagerFactory(
    credentials,
    "http://cn-hangzhou.oas.aliyuncs.com"
    ).withNumConcurrence(5).withMaxRetryTimePerRequest(3);

注:为避免文档中代码的冗长,本文设计的样例代码,会省去ArchiveManager的实例化。

配置ArchiveManager

同步接口和异步接口

ArchiveManager中按交互模式,分成同步接口(阻塞式)和异步接口(非阻塞)两大类。同步接口会阻塞当前运行的程序,异步接口则反之。在使用过程中,建议使用异步接口,通过实现监听类来获取任务中间状态。用户很容易通过方法的名称来区分这两类接口,比如:
  1. upload(…)即为同步式上传接口。
  2. uploadAsync(…)即为异步式上传接口。

执行结果

下面的章节,将通过业务类型的划分,来分别介绍各种业务接口的具体定义。

上传Archive

普通上传

用户通过接口上传文件到指定名称的Vault,可选的description参数是Archive的描述字段。使用普通上传接口时,用户无需关心是否开启Multipart,SDK会根据文件的大小选择适合用户的方式进行上传,两个普通上传接口定义如下:

/**
 * 上传文件到指定的vault中
 * @param vaultName
 *            目标vault的名称
 * @param file
 *            需要上传的文件对象
 * @return 返回UploadResult对象
 * @throws OASClientException
 *             客户端异常
 * @throws OASServerException
 *             服务端异常
 */
public UploadResult upload(String vaultName, File file)
    throws OASClientException, OASServerException;

/**
 * 上传文件到指定的vault中
 * @param vaultName
 *            目标vault的名称
 * @param file
 *            需要上传的文件对象
 * @param description
 *            上传archive的备注
 * @return 返回UploadResult对象
 * @throws OASClientException
 *             客户端异常
 * @throws OASServerException
 *             服务端异常
 */
public UploadResult upload(String vaultName, File file, String description)
    throws OASClientException, OASServerException;

文件上传示例:

File file = new File("[pathToYourFile]");
ServiceCredentials credentials = new ServiceCredentials(
    DemoConstants.ACCESS_ID,
    DemoConstants.ACCESS_KEY);

// 通过工厂类获得archiveManager接口
ArchiveManager archiveManager = OASFactory.archiveManagerFactory(credentials);

// java sdk 会帮助用户完成 vaultName 到 vaultId 的转换
// 由此避免了用户对一长串vaultId的记忆负担
UploadResult result = archiveManager
    .upload("[yourVaultName]", file, "[Description(not necessary)]");
String archiveId = result.getArchiveId();
System.out.println("Archive ID=" + archiveId);

注:上述的两个upload接口,已能满足用户的大部分需求。

使用Multipart进行上传

用户可以指定使用Multipart模式进行上传,完成一个Multipart上传需要两步:

  1. 初始化Multipart上传,获得UploadId。
  2. 根据获得的UploadId,上传数据。

关于UploadId的定义和作用,请参考接口说明中的详细介绍。

涉及到的接口定义如下:

/**
 * 手工初始化Multipart上传任务
 * @param vaultName 目标vault名称
 * @param file 需要上传的文件
 * @return 初始化成功的Multipart上传任务的UploadId字符串
 * @throws OASClientException
 * @throws OASServerException
 */
public String initiateMultipartUpload(String vaultName, File file)
    throws OASClientException, OASServerException;

/**
 * 手工初始化Multipart上传任务
 * @param vaultName 目标vault名称
 * @param file 需要上传的文件
 * @param description 文件描述
 * @return 初始化成功的Multipart上传任务的UploadId字符串
 * @throws OASClientException
 * @throws OASServerException
 */
public String initiateMultipartUpload(String vaultName, File file, String description)
    throws OASClientException, OASServerException;

/**
 * 根据具体的UploadId上传文件。
 * 要配合 {@link initiateMultipartUpload} 使用
 * @param vaultName 目标vault名称
 * @param file 需要上传的文件
 * @param uploadId 初始化完成的Multipart的UploadId
 * @return 上传结果对象
 * @throws OASClientException
 * @throws OASServerException
 */
public UploadResult uploadWithUploadId(String vaultName, File file, String uploadId)
    throws OASClientException, OASServerException;

示例代码:

File file = new File("[pathToYourFile]");
String vaultName = "[yourVaultName]";

//获得uploadId
//文件大小必须大于100MB,否则会抛异常提示用普通上传接口进行上传
String uploadId = archiveManager.initiateMultipartUpload(vaultName, file, "Hello OAS!");
//如果是已有的uploadId,直接使用之前获取过的uploadId
//String uploadId = "[uploadIdYouAlreadyHave]";
System.out.println("Get uploadId=" + uploadId);
UploadResult uploadResult = archiveManager.uploadWithUploadId(vaultName, file, uploadId);
System.out.println("Archive ID=" + uploadResult.getArchiveId());

使用已有的uploadId进行再次上传时,支持该任务的续传。例如,当一个Multipart任务在上传过程中被中止,这时,一些part已上传到OAS,一些part未完成。只要文件内容没有变化,根据保存下来的uploadId,可以继续上传这个文件剩余的part,已上传完毕的part不会重复上传。

如果开启了logger功能(如何配置logger请参考低级接口),在日志中会看到以下信息:

....
Range 33554432-67108863 got from OAS server, contentEtag=BEC7D9E8EC48FFD9C3859C10B64B4EDF, treeEtag=8DA41EA468F5613080A3D14857EFEFFE
Range 33554432-67108863 has uploaded, contentEtag=BEC7D9E8EC48FFD9C3859C10B64B4EDF, treeEtag=8DA41EA468F5613080A3D14857EFEFFE
Range 33554432-67108863 is the same as remote,ignore it. md5=BEC7D9E8EC48FFD9C3859C10B64B4EDF,etagTree=8DA41EA468F5613080A3D14857EFEFFE
....

只有当这个part的md5和etagTree的校验信息一致时,SDK才会忽略它的上传动作,否则,这个part会被重传。日志中会出现

....
Range 33554432-67108863 local Etag data is different from remote. It will be uploaded again!
....

下载

在OAS服务协议中,下载是通过提交任务(Job)的形式存在的,按业务类型分为三类,分别是:

  1. 提交下载Archive任务。
  2. 提交下载Inventory任务。
  3. 下载Job输出。

用户需要提交Job来获得他需要的数据,一个Job从创建到完成需要经历三步骤:

  1. 用户通过接口提交相应类型的Job;
  2. OAS 接收到Job并安排其执行;
  3. 用户将已完成Job的输出内容下载到本地。

:Job在OAS服务端是异步执行的,一个Job从提交到执行完毕这个过程一般耗时在几分钟到四小时之间。所以,所有下载类接口都是 异步接口 即全部以 Async 结尾。更多关于Job的信息,参考接口说明

提交下载Archive Job

接口定义如下:

/**
 * 提交请求下载archive任务请求
 * @param vaultName 目标vault名称
 * @param archiveId 需下载的archiveId
 * @return 返回Job信息跟踪器,可用来获取Job信息,如jobId,eTag校验信息
 * @throws OASClientException
 * @throws OASServerException
 */
public JobMonitor downloadAsync(String vaultName, String archiveId)
    throws OASClientException, OASServerException;

/**
 * 提交请求下载archive任务请求
 * @param vaultName 目标vault名称
 * @param archiveId 需下载的archiveId
 * @param description 备注
 * @return 返回Job信息跟踪器,可用来获取Job信息,如jobId,eTag校验信息
 * @throws OASClientException
 * @throws OASServerException
 */
public JobMonitor downloadAsync(String vaultName, String archiveId, String description)
    throws OASClientException, OASServerException;

/**
 * 提交请求下载archive任务请求
 * @param vaultName 目标vault名称
 * @param archiveId 需下载的archiveId
 * @param range 指定要下载的范围
 * @return 返回Job信息跟踪器,可用来获取Job信息,如jobId,eTag校验信息
 * @throws OASClientException
 * @throws OASServerException
 */
public JobMonitor downloadWithRangeAsync(String vaultName, String archiveId, Range range)
    throws OASClientException, OASServerException;

/**
 * 提交请求下载archive任务请求
 * @param vaultName 目标vault名称
 * @param archiveId 需下载的archiveId
 * @param range 指定要下载的范围
 * @param description 备注
 * @return 返回Job信息跟踪器,可用来获取Job信息,如jobId,eTag校验信息
 * @throws OASClientException
 * @throws OASServerException
 */
public JobMonitor downloadWithRangeAsync(
    String vaultName, String archiveId, Range range, String description)
    throws OASClientException, OASServerException;

示例代码:

JobMonitor jobMonitor = archiveManager.downloadAsync(
    "[yourVaultName]", "[yourArchiveId]");

// JobMonitor提供的阻塞等待方法,直到Job状态完结
// 实际场景中,不建议如此使用,因为Job的执行时间在几分钟到4小时不等
// 长时间阻塞程序不是明智的做法
jobMonitor.waitUntilFinished();
System.out.println("Job has been comitted successfully.");
System.out.println("Your JobId=" + jobMonitor.getJobId());

关于JobMonitor的具体使用方法,请阅读本文档 下载 章节的最后一部分 JobMonitor类使用讲解

提交Inventory任务

根据给定的Vault的名称, 提交Iventory任务后,OAS将准备好该Vault所包含文件的最后一次扫描信息,待Job执行完毕,可以将Vault的文件信息下载到本地。接口定义:

/**
 * 根据vault名称获得vault的目录信息
 * @param vaultName 目标vault名称
 * @return
 */
public JobMonitor downloadInventoryAsync(String vaultName)
    throws OASClientException, OASServerException;

/**
 * 根据vault名称获得vault的目录信息
 * @param vaultName 目标vault名称
 * @param description 备注
 * @return
 * @throws OASClientException
 * @throws OASServerException
 */
public JobMonitor downloadInventoryAsync(
    String vaultName, String description)
    throws OASClientException, OASServerException;

下载Job输出

根据给定的 Vault名称和JobID,下载Job的结果至本地文件。接口定义:

/**
 * 下载Job输出到文件,只有当Job的状态是Succeeded或Failed(到达终结状态)时,才能执行此操作
 * @param vaultName 目标vault名称
 * @param jobId 要下载的jobId
 * @param file 保存的目标文件
 * @return 保存的目标文件的句柄对象
 * @throws OASClientException
 * @throws OASServerException
 */
public File downloadJobOutput(String vaultName, String jobId, File file)
    throws OASClientException, OASServerException;

/**
 * 下载Job输出到文件,只有当Job的状态是Succeeded或Failed(到达终结状态)时,才能执行此操作
 * @param vaultName 目标vault名称
 * @param jobId 要下载的jobId
 * @param file 保存的目标文件
 * @return 异步任务传输对象,可获得传输信息
 * @throws OASClientException
 * @throws OASServerException
 */
public BaseTransfer<File> downloadJobOutputAsync(
    String vaultName, String jobId, File file)
       throws OASClientException, OASServerException;

JobMonitor类使用讲解

所有的下载动作,都是通过提交Job、下载Job输出的步骤完成。JobMonitor类是OAS JAVA SDK封装好,供用户跟踪Job信息的类,下面对JobMonitor类具体使用进行介绍。

  • waitUntilFinished()方法: 功能:阻塞住程序,每个1分钟请求OAS服务更新JobMonitor中Job的状态,直到Job的状态为成功或者失败,取消阻塞,例如下面的代码,虽然这样使用是==不推荐==的:

      // JobMonitor提供的阻塞等待方法,直到Job状态完结
      // 注意,本方法不建议用户使用,因为一个Job从提交到完成,一般需要几分钟到4个小时不等
      // 长期阻塞程序,不是一个明智的选择
      // 这里的使用仅仅起到示例作用
      jobMonitor.waitUntilFinished();
      System.out.println("Job has ended.");
      System.out.println("JobId=" + jobMonitor.getJobId() + ", JobStatus="
                             + jobMonitor.getJobStatus() + ", TreeEtag="
                             + jobMonitor.getDescriptor().getTreeEtag());
  • checkJobFinishedWithRefresh()方法: 功能:向OAS服务发送一个请求,获取Job的最新状态,并判断最新状态是否为成功、失败中的一个,如果是,返回true,否则返回false,示例代码如下,由于也会长期阻塞程序,这种用法也是 不推荐 的;

      while (true) {
          if (jobMonitor.checkJobFinishedWithRefresh()) {
              System.out.println("Job has ended.");
              System.out.println("JobId=" +
                    jobMonitor.getJobId() + ", JobStatus="
                    + jobMonitor.getJobStatus() + ", TreeEtag="
                    + jobMonitor.getDescriptor().getTreeEtag());
              break;
          } else {
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  logger.error("ERROR!", e);
              }
          }
      }
  • 推荐的JobMonitor的使用方法: 当使用ArchiveManager提交了Job后,可以从接口返回的JobMonitor中实时获取到本次任务的jobId,同样,ArchiveManager也提供了接口,根据jobId和vaultName实例化JobMonitor的方法:

      /**
       * 实例化JobMonitor
       * @param vaultName 目标vault名称
       * @param jobId 要实例化的jobId
       * @return
       * @throws OASClientException
       * @throws OASServerException
       */
      public JobMonitor getJobMonitor(String vaultName, String jobId)
          throws OASClientException, OASServerException;

    JobMonitor的实例化可以在用户程序的任何地方,随后用checkJobFinishedWithRefresh()等方法来检查Job的状态是否扭转,几个推荐的使用场景:

    1. 定时执行的任务中 (例如:可以每个小时扫描上一小时提交的Job的状态,再对Job输出进行下载);
    2. 某些业务事件被触发后(例如:业务中依赖这次Job输出的内容,否则该业务事件就无法执行);
    3. 用户触发。
  • 其他getter方法: 该类中有很多get方法,可以获得JobId(getJobId()方法)、JobStatus(getJobStatus()方法)等信息,但是这些方法获得的仅仅是本地的缓存,有可能和OAS服务端的信息不一致,如果需要手动从OAS服务端获取最新信息,可以执行 refreshJobDescriptorFromRemote()方法来实现。

异步接口中间过程的监听

异步接口与同步接口不同的是方法的返回值,一般来说,返回的对象是BaseTransfer,用户可通过ProgressListener监听进度,SpeedListener监听速度,通过start开始任务,stop停止任务,而waitUntilFinished将会阻塞直至任务完成,getResult可获取上传或下载结果。具体使用请参考本节的示例。

ProgressListenerSpeedListenerJobStatusListener分别为进度、速度、任务状态监听器,各个监听器的每个接口含义见下文的例子,而ProgressListenerAdapterSpeedListenerAdapterJobStatusListenerAdapter分别实现了以上接口,出错时默认会自动重试3次。特别注意的是,监听器部分接口的调用是在IO线程中,在监听器中执行费时的操作将有可能导致网络连接出错甚至中断。

以上传为例

ServiceCredentials credentials = new ServiceCredentials(
    DemoConstants.ACCESS_ID,
    DemoConstants.ACCESS_KEY);

// 通过工厂类获得archiveManager接口
ArchiveManager archiveManager = OASFactory.archiveManagerFactory(credentials);
File file = new File("[pathToYourFile]");

final BaseTransfer<UploadResult> bt = archiveManager.uploadAsync(
    "[yourVaultName]", file);

//设置最大并发数,默认为3,最大为10
bt.setNumConcurrence(5);

bt.addProgressListener(new ProgressListener() {

    @Override
    public void onStart(String id) {
        // 任务开始时调用,其中id为Multipart Upload任务ID,对于一般上传任务id为空
        System.out.println("Start! Upload ID: " + id);
    }

    @Override
    public boolean onError(Range range, Throwable t) {
        // 出错时调用,range是出错的字节范围,t是相应的错误
        // 当返回true时,BaseTransfer会进行重试,false则放弃
        System.out.println("ERROR!!!");
        return false;
    }

    @Override
    public void onCompleted() {
        // 任务完成时调用
        System.out.println("Upload complete");
    }

    @Override
    public void onProgressed(long current, long total) {
        // 上传进度,total为文件字节大小,current为当前已上传字节数
        System.out.println("Progress: " + current + " / " + total);
    }
});

bt.start();

final Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {

    @Override
    public void run() {
        System.out.println("Running time: " + bt.getRunningTime() + " seconds");
        System.out.println("Completed size: " + bt.getSizeCompleted() + " bytes");
        System.out.println("Total size: " + bt.getSizeTotal() + " bytes");
        System.out.println("Average speed: " + bt.getAverageSpeedInBytesPerSecond()
                           + " B/s");
        if (bt.isComplete()) {
            timer.cancel();
            synchronized (bt) {
                bt.notify();
            }
        }
    }

}, 0, 1000);

synchronized (bt) {
    try {
        bt.wait();
    } catch (InterruptedException e) {
        logger.error("", e);
    }
}

// 任务结束
System.out.println("=============================");
System.out.println("Running time: " + bt.getRunningTime() + " seconds");
System.out.println("Completed size: " + bt.getSizeCompleted() + " bytes");
System.out.println("Total size: " + bt.getSizeTotal() + " bytes");
System.out.println("Average speed: " + bt.getAverageSpeedInBytesPerSecond() + " B/s");
UploadResult uploadResult = bt.getResult();
System.out.println("Archive ID: " + uploadResult.getArchiveId());
System.out.println("ContentEtag: " + uploadResult.getContentEtag());

异常

根据出错原因的不同,SDK把异常分为两种不同类型,分别为OAServerErrorOASClientError

OASServerError

OASServerException是指一次完整的HTTP请求中,服务器返回了错误响应。各个成员变量的含义见下表。具体错误信息请参阅API文档第5节错误响应。

成员变量类型含义
statusCodeintHTTP状态码
requestIdString出错的请求的ID值,见API文档2.3.3节
errorCodeString错误代码,见API文档2.3.3节
errorMessageString错误信息,见API文档2.3.3节
errorTypeErrorType错误类型,见API文档2.3.3节

OASClientErrror

OASClientException表示客户端异常,可能原因包括网络连接出错、文件读写出错等,具体出错原因可通过查看成员变量message获得。异常仅作为标记错误类型,没有实现额外的方法。

自定义配置

高级接口ArchiveManager和低级接口AliyunOASClient一样,支持配置自定义,ServiceHost和ClientConfiguration的具体配置参考低级接口