public void run() {
   int offset = recoveryFromRecord();
   try {
     file = new RandomAccessFile(f, "r");
   } catch (FileNotFoundException e) {
     e.printStackTrace();
     completionHandler.complete(key, ResponseInfo.fileError(e), null);
     return;
   }
   nextTask(offset, 0, config.up.address);
 }
 /**
  * 创建块,并上传第一个分片内容
  *
  * @param address 上传主机
  * @param offset 本地文件偏移量
  * @param blockSize 分块的块大小
  * @param chunkSize 分片的片大小
  * @param progress 上传进度
  * @param _completionHandler 上传完成处理动作
  */
 private void makeBlock(
     URI address,
     int offset,
     int blockSize,
     int chunkSize,
     ProgressHandler progress,
     CompletionHandler _completionHandler,
     UpCancellationSignal c) {
   String path = format(Locale.ENGLISH, "/mkblk/%d", blockSize);
   try {
     file.seek(offset);
     file.read(chunkBuffer, 0, chunkSize);
   } catch (IOException e) {
     completionHandler.complete(key, ResponseInfo.fileError(e), null);
     return;
   }
   this.crc32 = Crc32.bytes(chunkBuffer, 0, chunkSize);
   URI u = newURI(address, path);
   post(u, chunkBuffer, 0, chunkSize, progress, _completionHandler, c);
 }
 private void putChunk(
     URI address,
     int offset,
     int chunkSize,
     String context,
     ProgressHandler progress,
     CompletionHandler _completionHandler,
     UpCancellationSignal c) {
   int chunkOffset = offset % Configuration.BLOCK_SIZE;
   String path = format(Locale.ENGLISH, "/bput/%s/%d", context, chunkOffset);
   try {
     file.seek(offset);
     file.read(chunkBuffer, 0, chunkSize);
   } catch (IOException e) {
     completionHandler.complete(key, ResponseInfo.fileError(e), null);
     return;
   }
   this.crc32 = Crc32.bytes(chunkBuffer, 0, chunkSize);
   URI u = newURI(address, path);
   post(u, chunkBuffer, 0, chunkSize, progress, _completionHandler, c);
 }
 private boolean isNotQiniu(ResponseInfo info) {
   return info.isNotQiniu() && !token.hasReturnUrl();
 }
  private void nextTask(final int offset, final int retried, final URI address) {
    if (isCancelled()) {
      ResponseInfo i = ResponseInfo.cancelled();
      completionHandler.complete(key, i, null);
      return;
    }

    if (offset == size) {
      CompletionHandler complete =
          new CompletionHandler() {
            @Override
            public void complete(ResponseInfo info, JSONObject response) {
              if (info.isOK()) {
                removeRecord();
                options.progressHandler.progress(key, 1.0);
                completionHandler.complete(key, info, response);
                return;
              }

              if ((isNotQiniu(info) || info.needRetry()) && retried < config.retryMax) {
                nextTask(offset, retried + 1, config.upBackup.address);
                return;
              }
              completionHandler.complete(key, info, response);
            }
          };
      makeFile(address, complete, options.cancellationSignal);
      return;
    }

    final int chunkSize = calcPutSize(offset);
    ProgressHandler progress =
        new ProgressHandler() {
          @Override
          public void onProgress(int bytesWritten, int totalSize) {
            double percent = (double) (offset + bytesWritten) / size;
            if (percent > 0.95) {
              percent = 0.95;
            }
            options.progressHandler.progress(key, percent);
          }
        };

    CompletionHandler complete =
        new CompletionHandler() {
          @Override
          public void complete(ResponseInfo info, JSONObject response) {
            if (!info.isOK()) {
              if (info.statusCode == 701 && retried < config.retryMax) {
                nextTask(
                    (offset / Configuration.BLOCK_SIZE) * Configuration.BLOCK_SIZE,
                    retried + 1,
                    address);
                return;
              }

              if ((isNotQiniu(info) || info.needRetry()) && retried < config.retryMax) {
                nextTask(offset, retried + 1, config.upBackup.address);
                return;
              }

              completionHandler.complete(key, info, response);
              return;
            }
            String context = null;

            if (response == null && retried < config.retryMax) {
              nextTask(offset, retried + 1, config.upBackup.address);
              return;
            }
            long crc = 0;
            try {
              context = response.getString("ctx");
              crc = response.getLong("crc32");
            } catch (JSONException e) {
              e.printStackTrace();
            }
            if ((context == null || crc != ResumeUploader.this.crc32)
                && retried < config.retryMax) {
              nextTask(offset, retried + 1, config.upBackup.address);
              return;
            }
            contexts[offset / Configuration.BLOCK_SIZE] = context;
            record(offset + chunkSize);
            nextTask(offset + chunkSize, retried, address);
          }
        };
    if (offset % Configuration.BLOCK_SIZE == 0) {
      int blockSize = calcBlockSize(offset);
      makeBlock(
          address, offset, blockSize, chunkSize, progress, complete, options.cancellationSignal);
      return;
    }
    String context = contexts[offset / Configuration.BLOCK_SIZE];
    putChunk(address, offset, chunkSize, context, progress, complete, options.cancellationSignal);
  }