应用场景
对象数据在上传下载过程中,有可能会因为网络劫持、数据缓存等原因,存在数据不一致的问题。
方案架构
OBS提供通过计算MD5、crc64对上传下载的数据进行完整性校验。默认情况下,OBS不会进行完整性校验,您可以在上传下载时主动启用校验,具体使用方式参见表1。
- crc64、MD5的完整性校验结果不互通,例如如果您使用crc64方式在上传时通过了完整性校验,那必须使用crc64方式在下载时校验完整性。
- 下载对象时,只有当待下载对象具有crc64/MD5值时,校验才会生效。
- 启用crc64/MD5进行数据完整性校验会影响上传下载性能。
表1 校验数据完整性的方式 | 校验方式 | 说明 | 支持的接口 |
| crc64 | 根据ECMA-182标准计算得出crc64值,通过对比客户端与服务端crc64值判断数据完整性。 分段上传场景,如果所有分段都有crc64值,那么服务端返回的crc64值是整个对象的crc64值。如果有分段没有crc64值,那么服务端将不返回crc64值。 | |
| MD5 | 根据MD5算法得出MD5值,通过对比客户端与服务端MD5判断数据完整性。 分段上传场景,服务端计算得出的MD5值不是对象本身的MD5值,分段上传场景建议使用crc64校验。 | 普通上传:PutObject、PostObject |
上传对象时,OBS会先在客户端计算出对象的crc64/MD5值然后携带上传至OBS,OBS服务端再根据上传的对象内容计算出crc64/MD5值,最终与携带上传的crc64/MD5值进行对比,如果对比结果一致,对象上传成功,否则上传失败。使用crc64/MD5值对上传数据进行完整性校验的示意图如图1所示。
图1 校验上传对象的完整性
下载对象时,OBS会将对象已有的crc64/MD5值与根据下载的对象内容计算出来的crc64/MD5值进行对比,如果对比结果一致,对象下载成功,否则下载失败。使用crc64/MD5值对下载数据进行完整性校验的示意图如图2所示。
图2 校验下载对象的完整性
示例代码
以下Java示例代码演示如何在上传对象时基于crc64值校验数据完整性,了解更多请参考上传时使用crc64校验文件一致性(Java SDK)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 | import static com.obs.services.internal.Constants.CommonHeaders.HASH_CRC64ECMA;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.PutObjectRequest;
import com.obs.services.model.PutObjectResult;
import java.io.File;
import java.util.Map;
public class PutObjectWithCRC64{
public static void main(String[] args) {
// 您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入。如果使用硬编码可能会存在泄露风险。
// 您可以登录访问管理控制台获取访问密钥AK/SK
String ak = System.getenv("ACCESS_KEY_ID");
String sk = System.getenv("SECRET_ACCESS_KEY_ID");
// 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。
// 您可以通过环境变量获取访问密钥AK/SK/SecurityToken,也可以使用其他外部引入方式传入。
// String securityToken = System.getenv("SECURITY_TOKEN");
// endpoint填写桶所在的endpoint, 此处以华北-北京四为例,其他地区请按实际情况填写。
String endPoint = "https://obs.cn-north-4.myhuaweicloud.com";
// endpoint填写桶所在的endpoint, 此处以中国-香港为例,其他地区请按实际情况填写。
String endPoint = "https://obs.ap-southeast-1.myhuaweicloud.com";
// endpoint填写桶所在区域的endpoint。
String endPoint = "https://your-endpoint";
// 您可以通过环境变量获取endPoint,也可以使用其他外部引入方式传入。
//String endPoint = System.getenv("ENDPOINT");
// 创建ObsClient实例
// 使用永久AK/SK初始化客户端
ObsClient obsClient = new ObsClient(ak, sk, endPoint);
// 使用临时AK/SK和SecurityToken初始化客户端
// ObsClient obsClient = new ObsClient(ak, sk, securityToken, endPoint);
try {
// 上传本地文件
String exampleBucket = "examplebucket";
String localFilePath = "localfile";
String exampleObjectKey = "objectKey";
PutObjectRequest putObjectRequest = new PutObjectRequest(exampleBucket, exampleObjectKey, new File(localFilePath));
putObjectRequest.setNeedCalculateCRC64(true);
PutObjectResult putObjectResult = obsClient.putObject(putObjectRequest);
System.out.println(
"Server returned crc64 string:" + putObjectResult.getResponseHeaders().get(HASH_CRC64ECMA));
System.out.println("PutObject successfully");
} catch (ObsException e) {
System.out.println("PutObject failed");
// 请求失败,打印http状态码
System.out.println("HTTP Code:" + e.getResponseCode());
// 请求失败,打印服务端错误码
System.out.println("Error Code:" + e.getErrorCode());
// 请求失败,打印详细错误信息
System.out.println("Error Message:" + e.getErrorMessage());
// 请求失败,打印请求id
System.out.println("Request ID:" + e.getErrorRequestId());
System.out.println("Host ID:" + e.getErrorHostId());
// 异常场景下打印所有头域,可能包含报错相关信息
Map<String, String> headers = e.getResponseHeaders();
if(headers != null){
for (Map.Entry<String, String> header : headers.entrySet()) {
System.out.println(header.getKey()+":"+header.getValue());
}
}
e.printStackTrace();
} catch (Exception e) {
System.out.println("PutObject failed");
// 其他异常信息打印
e.printStackTrace();
}
}
}
|
前提条件
待下载对象已有crc64值,如果没有将不会进行完整性校验,对象的crc64值需要在上传的时候计算并设置。
示例代码
以下Java示例代码演示如何在下载对象时基于crc64值校验数据完整性,了解更多请参考下载时使用crc64校验文件一致性(Java SDK)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 | import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.DownloadFileRequest;
import com.obs.services.model.DownloadFileResult;
public class DownloadFile001 {
public static void main(String[] args) {
// 您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入。如果使用硬编码可能会存在泄露风险。
// 您可以登录访问管理控制台获取访问密钥AK/SK
String ak = System.getenv("ACCESS_KEY_ID");
String sk = System.getenv("SECRET_ACCESS_KEY_ID");
// 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。
// 您可以通过环境变量获取访问密钥AK/SK/SecurityToken,也可以使用其他外部引入方式传入。
// String securityToken = System.getenv("SECURITY_TOKEN");
// endpoint填写桶所在的endpoint, 此处以华北-北京四为例,其他地区请按实际情况填写。
String endPoint = "https://obs.cn-north-4.myhuaweicloud.com";
// endpoint填写桶所在的endpoint, 此处以中国-香港为例,其他地区请按实际情况填写。
String endPoint = "https://obs.ap-southeast-1.myhuaweicloud.com";
// endpoint填写桶所在区域的endpoint。
String endPoint = "https://your-endpoint";
// 您可以通过环境变量获取endPoint,也可以使用其他外部引入方式传入。
//String endPoint = System.getenv("ENDPOINT");
// 创建ObsClient实例
// 使用永久AK/SK初始化客户端
ObsClient obsClient = new ObsClient(ak, sk, endPoint);
// 使用临时AK/SK和SecurityToken初始化客户端
// ObsClient obsClient = new ObsClient(ak, sk, securityToken, endPoint);
try {
DownloadFileRequest request = new DownloadFileRequest("examplebucket", "objectname");
// 设置下载对象的本地文件路径
request.setDownloadFile("localfile");
// 设置分段下载时的最大并发数
request.setTaskNum(5);
// 设置分段大小为10MB
request.setPartSize(10 * 1024 * 1024);
// 开启断点续传模式
request.setEnableCheckpoint(true);
// 开启校验文件CRC64
request.setNeedCalculateCRC64(true);
// 进行断点续传下载
DownloadFileResult result = obsClient.downloadFile(request);
System.out.println("Server returned crc64 string:" + result.getObjectMetadata().getCrc64());
System.out.println("downloadFile successfully");
} catch (ObsException e) {
System.out.println("downloadFile failed");
// 请求失败,打印http状态码
System.out.println("HTTP Code:" + e.getResponseCode());
// 请求失败,打印服务端错误码
System.out.println("Error Code:" + e.getErrorCode());
// 请求失败,打印详细错误信息
System.out.println("Error Message:" + e.getErrorMessage());
// 请求失败,打印请求id
System.out.println("Request ID:" + e.getErrorRequestId());
System.out.println("Host ID:" + e.getErrorHostId());
e.printStackTrace();
} catch (Exception e) {
System.out.println("downloadFile failed");
// 其他异常信息打印
e.printStackTrace();
}
}
}
|
使用MD5校验上传对象的完整性
obsutil、OBS Browser+以及OBS SDK都支持在上传对象时进行完整性校验,您可以根据自己的业务选择任意一种方式进行校验。下面分别介绍了几种方式在上传对象时进行完整性校验的操作指导。
方式一:使用obsutil校验上传对象的完整性
obsutil支持在上传对象时通过附加参数(vmd5)来校验数据的完整性。命令行工具,可以通过简单的一行命令实现上传下载,并且在命令中选择是否采用MD5校验。
以在Windows操作系统上传本地一个位于D盘的test.txt文件至mytestbucket桶为例,开启完整性校验的命令示例如下:
obsutil cp D:\test.txt obs://mytestbucket/test.txt -vmd5
校验通过后,对象上传成功,系统显示Upload successfully的回显信息。

方式二:使用OBS Browser+校验上传对象的完整性
OBS Browser+图形化界面工具,可以一键开启或关闭MD5校验,同时提供任务管理,方便查看校验状态。OBS Browser+默认关闭MD5校验,在OBS Browser+上启用MD5校验完整性并上传对象的步骤如下:
- 登录OBS Browser+。
- 单击客户端右上方的
,并选择“高级设置”。 - 勾选“MD5校验”,如图3所示。
图3 配置MD5校验
- 单击“确定”。
- 选择待上传文件的桶,上传文件。
- 如果MD5校验成功,则文件上传成功。
- 如果MD5校验失败,则文件上传失败,且在任务管理中提示失败原因:校验文件MD5失败。
方式三:使用OBS SDK校验上传对象的完整性
开发者可以通过OBS SDK进行二次开发,自行判断MD5校验结果,并根据实际业务进行结果处理。
OBS提供Java、Python等多种语言的SDK,各SDK通过在上传对象时设置对象的Content-MD5值以开启完整性校验。如何计算并设置对象MD5值请前往OBS SDK参见各自开发指南的setObjectMetadata接口。
此处以使用OBS Java SDK上传Windows本地D盘一个名为text.txt的文本文件至mytestbucket为例,上传过程使用MD5值校验数据完整性的示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | String endPoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量ACCESS_KEY_ID和SECRET_ACCESS_KEY_ID。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
String ak = System.getenv("ACCESS_KEY_ID");
String sk = System.getenv("SECRET_ACCESS_KEY_ID");
// 创建ObsClient实例
ObsClient obsClient = new ObsClient(ak, sk, endPoint);
// 计算并设置MD5值
ObjectMetadata metadata = new ObjectMetadata();
File file = new File("D:\\text.txt");
FileInputStream fis = new FileInputStream(file);
InputStream is = (InputStream)fis;
String contentMd5 = obsClient.base64Md5(is);
metadata.setContentMd5(contentMd5);
// 带MD5值上传文件
obsClient.putObject("mytestbucket", "text.txt", file, metadata);
|
- 对象数据的MD5值必须经过Base64编码。
- OBS服务端会将该MD5值与对象数据计算出的MD5值进行对比,如果不匹配则上传失败,返回HTTP 400错误。如果匹配,对象上传成功,返回HTTP 200状态码。
使用MD5校验下载对象的完整性
OBS Browser+、obsutil以及OBS SDK都支持在下载对象时进行完整性校验,您可以根据自己的业务选择任意一种方式进行校验,本文就几种方式如何使用完整性校验进行了详细说明。
前提条件
待下载对象已有MD5值,如果没有MD5值,将不会进行完整性校验,对象的MD5值需要在上传的时候计算并设置。
方式一:使用obsutil校验下载对象的完整性
obsutil支持在下载对象时通过附加参数(vmd5)来校验下载数据的完整性。
以在Windows操作系统下载mytestbucket桶中的test.txt文件至本地为例,开启数据完整性校验的步骤如下:
- 执行以下命令,检查待下载对象是否具有MD5信息。
obsutil stat obs://test-bucket/test.txt
- 返回的对象基本信息中,包含MD5信息,如下图所示,执行步骤2。

- 不包含MD5信息,下载对象时无法进行完整性校验。
- 执行以下命令,下载对象。
obsutil cp obs://mytestbucket/test.txt D:\test.txt -vmd5
- 对象下载成功且通过完整性校验,回显信息如下:

- 如果桶中对象没有MD5值,对象能够下载成功,但不会校验完整性,回显信息如下:

方式二:使用OBS Browser+校验下载对象的完整性
OBS Browser+默认关闭MD5校验,在OBS Browser+上启用MD5校验完整性并下载对象的步骤如下:
- 登录OBS Browser+。
- 单击客户端右上方的
,并选择“高级设置”。 - 勾选“MD5校验”,如图4所示。
图4 配置MD5校验
- 单击“确定”。
- 选择待下载文件的桶,下载文件。
- 如果MD5校验成功,则文件下载成功。
- 如果MD5校验失败,则文件下载失败,且在任务管理中提示失败原因:校验文件MD5失败。
方式三:使用OBS SDK校验下载对象的完整性
OBS SDK对待下载对象的自定义元数据中的MD5值和下载到本地的对象的MD5值进行对比,通过对比结果判断下载对象的完整性。
此处以使用OBS Java SDK下载mytestbucket桶中一个名为test.txt的文本文件为例,下载过程使用MD5值校验数据完整性的示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | String endPoint = "https://your-endpoint";
// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量ACCESS_KEY_ID和SECRET_ACCESS_KEY_ID。
// 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html
String ak = System.getenv("ACCESS_KEY_ID");
String sk = System.getenv("SECRET_ACCESS_KEY_ID");
// 创建ObsClient实例
final ObsClient obsClient = new ObsClient(ak, sk, endPoint);
// 获取对象的MD5值
ObjectMetadata metadata = obsClient.getObjectMetadata("mytestbucket", "test.txt");
String md5Origin = metadata.getUserMetadata("contentMd5");
// 计算下载后对象的MD5值
Obsobject obsobject = obsClient.getObject("mytestbucket", "test.txt");
String md5Download = obsClient.base64Md5(obsobject.getObjectContent());
// 对比MD5值
if(md5Origin.contentEquals(md5Download))
System.out.println("Object MD5 validation passes!\n");
else
System.out.println("Object MD5 validation failed!\n");
|
在以上示例代码中,获取对象MD5值是在上传时设置的自定义元数据x-obs-meta-md5chksum,实际开发中需要根据自定义的元数据名称修改。