OSS 移动端SDK 上传文件的方式可以分为:简单上传,追加上传,断点续传。

简单上传

  • 调用同步接口上传:
    // 构造上传请求
    PutObjectRequest put = new PutObjectRequest("<bucketName>", "<objectKey>", "<uploadFilePath>");
    
    // 文件元信息的设置是可选的
    // ObjectMetadata metadata = new ObjectMetadata();
    // metadata.setContentType("application/octet-stream"); // 设置content-type
    // metadata.setContentMD5(BinaryUtil.calculateBase64Md5(uploadFilePath)); // 校验MD5
    // put.setMetadata(metadata);
    
    try {
    
    	PutObjectResult putResult = oss.putObject(put);
    
    	Log.d("PutObject", "UploadSuccess");
    
    	Log.d("ETag", putResult.getETag());
    	Log.d("RequestId", putResult.getRequestId());
    } catch (ClientException e) {
    	// 本地异常如网络异常等
    	e.printStackTrace();
    } catch (ServiceException e) {
    	// 服务异常
    	Log.e("RequestId", e.getRequestId());
    	Log.e("ErrorCode", e.getErrorCode());
    	Log.e("HostId", e.getHostId());
    	Log.e("RawMessage", e.getRawMessage());
    }
    
    注意 在Android中,不能在UI线程调用同步接口,只能在子线程调用,否则将出现异常。如果希望直接在UI线程中上传,请使用异步接口。
  • 调用异步接口上传:
    // 构造上传请求
    PutObjectRequest put = new PutObjectRequest("<bucketName>", "<objectKey>", "<uploadFilePath>");
    
    // 异步上传时可以设置进度回调
    put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
    	@Override
    	public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
    		Log.d("PutObject", "currentSize: " + currentSize + " totalSize: " + totalSize);
    	}
    });
    
    OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
    	@Override
    	public void onSuccess(PutObjectRequest request, PutObjectResult result) {
    		Log.d("PutObject", "UploadSuccess");
    
    		Log.d("ETag", result.getETag());
    		Log.d("RequestId", result.getRequestId());
    	}
    
    	@Override
    	public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
    		// 请求异常
    		if (clientExcepion != null) {
    			// 本地异常如网络异常等
    			clientExcepion.printStackTrace();
    		}
    		if (serviceException != null) {
    			// 服务异常
    			Log.e("ErrorCode", serviceException.getErrorCode());
    			Log.e("RequestId", serviceException.getRequestId());
    			Log.e("HostId", serviceException.getHostId());
    			Log.e("RawMessage", serviceException.getRawMessage());
    		}
    	}
    });
    
    // task.cancel(); // 可以取消任务
    // task.waitUntilFinished(); // 可以等待任务完成
    

简单上传二进制byte[]数组

byte[] uploadData = new byte[100 * 1024];
new Random().nextBytes(uploadData);

// 构造上传请求
PutObjectRequest put = new PutObjectRequest(testBucket, testObject, uploadData);

try {
	PutObjectResult putResult = oss.putObject(put);

	Log.d("PutObject", "UploadSuccess");

	Log.d("ETag", putResult.getETag());
	Log.d("RequestId", putResult.getRequestId());
} catch (ClientException e) {
	// 本地异常如网络异常等
	e.printStackTrace();
} catch (ServiceException e) {
	// 服务异常
	Log.e("RequestId", e.getRequestId());
	Log.e("ErrorCode", e.getErrorCode());
	Log.e("HostId", e.getHostId());
	Log.e("RawMessage", e.getRawMessage());
}
  • 上传到文件目录

    OSS服务是没有文件夹这个概念的,所有元素都是以文件来存储,但给用户提供了创建模拟文件夹的方式。创建模拟文件夹本质上来说是创建了一个名字以“/”结尾的文件,这个文件可以上传下载,只是控制台会对以“/”结尾的文件以文件夹的方式展示。

    在上传文件时,如果把ObjectKey写为"folder/subfolder/file",即是模拟了把文件上传到folder/subfolder/下的file文件。

    说明 OSS路径默认是"根目录",不需要以'/'开头。
  • 上传Content-Type设置

    在Web服务中Content-Type用来设定文件的类型,决定以什么形式、什么编码读取这个文件。某些情况下,对于上传的文件需要设定Content-Type,否则文件不能以自己需要的形式和编码来读取。使用SDK上传文件时,如果不指定Content-Type,SDK会帮您根据后缀自动添加Content-Type。

    // 构造上传请求
    PutObjectRequest put = new PutObjectRequest("<bucketName>", "<objectKey>", "<uploadFilePath>");
    
    ObjectMetadata metadata = new ObjectMetadata();
    // 指定Content-Type
    metadata.setContentType("application/octet-stream");
    // user自定义metadata
    metadata.addUserMetadata("x-oss-meta-name1", "value1");
    put.setMetadata(metadata);
    
    OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
    	...
    });
    
  • Append Object 追加上传

    Append Object以追加写的方式上传文件。通过Append Object操作创建的Object类型为Appendable Object,而通过Put Object上传的Object是Normal Object。

    AppendObjectRequest append = new AppendObjectRequest(testBucket, testObject, uploadFilePath);
    
    ObjectMetadata metadata = new ObjectMetadata();
    metadata.setContentType("application/octet-stream");
    append.setMetadata(metadata);
    
    // 设置追加位置
    append.setPosition(0);
    
    append.setProgressCallback(new OSSProgressCallback<AppendObjectRequest>() {
    	@Override
    	public void onProgress(AppendObjectRequest request, long currentSize, long totalSize) {
    		Log.d("AppendObject", "currentSize: " + currentSize + " totalSize: " + totalSize);
    	}
    });
    
    OSSAsyncTask task = oss.asyncAppendObject(append, new OSSCompletedCallback<AppendObjectRequest, AppendObjectResult>() {
    	@Override
    	public void onSuccess(AppendObjectRequest request, AppendObjectResult result) {
    		Log.d("AppendObject", "AppendSuccess");
    		Log.d("NextPosition", "" + result.getNextPosition());
    	}
    
    	@Override
    	public void onFailure(AppendObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
    		// 异常处理
    	}
    });
    

    用户使用Append方式上传,关键要对Position这个参数进行正确的设置:

    • 当用户创建一个Appendable Object时,追加位置设为0。
    • 当对Appendable Object进行内容追加时,追加位置设为Object当前长度。有两种方式获取该Object长度:
      • 通过上传追加后的返回内容获取。
      • 通过head object获取文件长度。
  • 上传后回调通知

    客户端在上传Object时可以指定OSS服务端在处理完上传请求后,通知您的业务服务器,在该服务器确认接收了该回调后将回调的结果返回给客户端。因为加入了回调请求和响应的过程,相比简单上传,使用回调通知机制一般会导致客户端花费更多的等待时间。

    具体说明参考:Callback

    代码示例:

    PutObjectRequest put = new PutObjectRequest(testBucket, testObject, uploadFilePath);
    
    put.setCallbackParam(new HashMap<String, String>() {
    	{
    		put("callbackUrl", "110.75.82.106/callback");
            put("callbackHost", "oss-cn-hangzhou.aliyuncs.com");
            put("callbackBodyType", "application/json");
    		put("callbackBody", "{\"mimeType\":${mimeType},\"size\":${size}}");
    	}
    });
    
    OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
    	@Override
    	public void onSuccess(PutObjectRequest request, PutObjectResult result) {
    		Log.d("PutObject", "UploadSuccess");
    
    		// 只有设置了servercallback,这个值才有数据
    		String serverCallbackReturnJson = result.getServerCallbackReturnBody();
    
    		Log.d("servercallback", serverCallbackReturnJson);
    	}
    
    	@Override
    	public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
    		// 异常处理
    	}
    });
    

    如果需要支持自定义参数,参考如下设置:

    put.setCallbackParam(new HashMap<String, String>() {
        {
            put("callbackUrl", "http://182.92.192.125/leibin/notify.php");
            put("callbackHost", "oss-cn-hangzhou.aliyuncs.com");
            put("callbackBodyType", "application/json");
            put("callbackBody", "{\"object\":${object},\"size\":${size},\"my_var1\":${x:var1},\"my_var2\":${x:var2}}");
        }
    });
    
    put.setCallbackVars(new HashMap<String, String>() {
        {
            put("x:var1", "value1");
            put("x:var2", "value2");
        }
    });
    

数据完整性校验

因为移动端网络环境的复杂性,OSS SDK提供了基于MD5和CRC64的端到端的数据完整性验证功能。

  • MD5校验

    需要在上传文件时提供文件的Content-MD5值,OSS服务器会帮助用户进行MD5校验,只有在OSS服务器计算接收到的文件得到的MD5值和上传提供的MD5一致时才可以上传成功,从而保证上传数据的一致性。

    // 构造上传请求
    PutObjectRequest put = new PutObjectRequest("<bucketName>", "<objectKey>", "<uploadFilePath>");
    
    ObjectMetadata metadata = new ObjectMetadata();
    metadata.setContentType("application/octet-stream");
    try {
    	// 设置Md5以便校验
    	metadata.setContentMD5(BinaryUtil.calculateBase64Md5("<uploadFilePath>")); // 如果是从文件上传
    	// metadata.setContentMD5(BinaryUtil.calculateBase64Md5(byte[])); // 如果是上传二进制数据
    } catch (IOException e) {
    	e.printStackTrace();
    }
    put.setMetadata(metadata);
    
    OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
    	...
    });
    
  • CRC校验

    与MD5相比,CRC64可以边上传边计算CRC值。

    // 构造上传请求
    PutObjectRequest put = new PutObjectRequest("<bucketName>", "<objectKey>", "<uploadFilePath>");
    // 开启crc效验后。如果在传输中数据不一致,会直接抛出ClientException异常。提示InconsistentException: inconsistent object
    put.setCRC64(OSSRequest.CRC64Config.YES);
    
    OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
         @Override
         public void onSuccess(PutObjectRequest request, PutObjectResult result) {
    	 //.....
         }
    
    	@Override
        public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
    	//.....
        	if (clientExcepion != null) {
            	// client side exception,  such as network exception
    			// 检查是否有InconsistentException: inconsistent object信息
                clientExcepion.getMessage();
             }
          }
    });