
# 基于crc64或MD5的OBS数据完整性校验
#### 应用场景
对象数据在上传下载过程中，有可能会因为网络劫持、数据缓存等原因，存在数据不一致的问题。
#### 方案架构
OBS提供通过计算MD5、crc64对上传下载的数据进行完整性校验。默认情况下，OBS不会进行完整性校验，您可以在上传下载时主动启用校验，具体使用方式参见[表1]。
![](https://support.huaweicloud.com/bestpractice-obs/public_sys-resources/note_3.0-zh-cn.png)
- crc64、MD5的完整性校验结果不互通，例如如果您使用crc64方式在上传时通过了完整性校验，那必须使用crc64方式在下载时校验完整性。
- 下载对象时，只有当待下载对象具有crc64/MD5值时，校验才会生效。
- 启用crc64/MD5进行数据完整性校验会影响上传下载性能。
 
 表1校验数据完整性的方式 
| **校验方式** | **说明**                                                                                                                                                         | **支持的接口**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
|:---|:---|:---|
| crc64    | 根据ECMA-182标准计算得出crc64值，通过对比客户端与服务端crc64值判断数据完整性。 分段上传场景，如果所有分段都有crc64值，那么服务端返回的crc64值是整个对象的crc64值。如果有分段没有crc64值，那么服务端将不返回crc64值。 | - 普通上传：[PUT上传-PutObject](https://support.huaweicloud.com/api-obs/obs_04_0080.html)、[POST上传-PostObject](https://support.huaweicloud.com/api-obs/obs_04_0081.html)  - 分段上传：[上传段-UploadPart](https://support.huaweicloud.com/api-obs/obs_04_0099.html)、[合并段-CompleteMultipartUpload](https://support.huaweicloud.com/api-obs/obs_04_0102.html)  - 追加上传：[追加写对象-AppendObject](https://support.huaweicloud.com/api-obs/obs_04_0088.html)（首次上传如果携带crc64值，则会写到元数据中，即便后续不携带，服务端也根据元数据中是否有crc64内容判断是否计算。如果首次不带，则后续都不计算。）  - 复制对象和拷贝段：[复制对象-CopyObject](https://support.huaweicloud.com/api-obs/obs_04_0082.html)、[拷贝段-CopyPart](https://support.huaweicloud.com/api-obs/obs_04_0100.html)（当源对象存在crc64值时，下载或获取目标对象元数据将返回crc64值）  - 下载和获取对象元数据：[下载对象-GetObject](https://support.huaweicloud.com/api-obs/obs_04_0083.html)、[获取对象元数据-HeadObject](https://support.huaweicloud.com/api-obs/obs_04_0084.html)（如果对象存在crc64值，则返回crc64，否则不返回）  - [Range下载](https://support.huaweicloud.com/api-obs/obs_04_0083.html)：返回完整对象的crc64值，不重新计算crc64值   |
| MD5      | 根据MD5算法得出MD5值，通过对比客户端与服务端MD5判断数据完整性。 分段上传场景，服务端计算得出的MD5值不是对象本身的MD5值，分段上传场景建议使用crc64校验。                                            | 普通上传：PutObject、PostObject                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
   
上传对象时，OBS会先在客户端计算出对象的crc64/MD5值然后携带上传至OBS，OBS服务端再根据上传的对象内容计算出crc64/MD5值，最终与携带上传的crc64/MD5值进行对比，如果对比结果一致，对象上传成功，否则上传失败。使用crc64/MD5值对上传数据进行完整性校验的示意图如[图1]所示。
图1校验上传对象的完整性   
![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001882055568.png "点击放大")
下载对象时，OBS会将对象已有的crc64/MD5值与根据下载的对象内容计算出来的crc64/MD5值进行对比，如果对比结果一致，对象下载成功，否则下载失败。使用crc64/MD5值对下载数据进行完整性校验的示意图如[图2]所示。
图2校验下载对象的完整性   
![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001927937397.png "点击放大")
#### 使用crc64校验上传对象的完整性
OBS支持通过API、SDK（[Java SDK](https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0624.html)、[Python SDK](https://support.huaweicloud.com/sdk-python-devg-obs/obs_22_0927.html)）使用crc64校验一致性。
#### 示例代码
以下Java示例代码演示如何在上传对象时基于crc64值校验数据完整性，了解更多请参考[上传时使用crc64校验文件一致性(Java SDK)](https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0625.html)。
```
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校验下载对象的完整性
OBS支持通过API、SDK（[Java SDK](https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0624.html)、[Python SDK](https://support.huaweicloud.com/sdk-python-devg-obs/obs_22_0927.html)）使用crc64校验一致性。
#### 前提条件
待下载对象已有crc64值，如果没有将不会进行完整性校验，对象的crc64值需要在上传的时候计算并设置。
#### 示例代码
以下Java示例代码演示如何在下载对象时基于crc64值校验数据完整性，了解更多请参考[下载时使用crc64校验文件一致性(Java SDK)](https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0627.html)。
```
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的回显信息。
![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001882035034.png "点击放大")
#### 方式二：使用OBS Browser+校验上传对象的完整性
OBS Browser+图形化界面工具，可以一键开启或关闭MD5校验，同时提供任务管理，方便查看校验状态。OBS Browser+默认关闭MD5校验，在OBS Browser+上启用MD5校验完整性并上传对象的步骤如下：
1. 登录OBS Browser+。
2. 单击客户端右上方的![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001927914317.png)，并选择"高级设置"。
3. 勾选"MD5校验"，如[图3]所示。
   
   图3配置MD5校验   
   ![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001927833917.png "点击放大")
   
   
4. 单击"确定"。
5. 选择待上传文件的桶，上传文件。 
   - 如果MD5校验成功，则文件上传成功。
   
   - 如果MD5校验失败，则文件上传失败，且在任务管理中提示失败原因：校验文件MD5失败。
   
   
   
   
 
#### 方式三：使用OBS SDK校验上传对象的完整性
开发者可以通过OBS SDK进行二次开发，自行判断MD5校验结果，并根据实际业务进行结果处理。
OBS提供Java、Python等多种语言的SDK，各SDK通过在上传对象时设置对象的Content-MD5值以开启完整性校验。如何计算并设置对象MD5值请前往[OBS SDK](https://support.huaweicloud.com/sdkreference-obs/obs_02_0001.html)参见各自开发指南的setObjectMetadata接口。
此处以使用OBS Java SDK上传Windows本地D盘一个名为text.txt的文本文件至mytestbucket为例，上传过程使用MD5值校验数据完整性的示例代码如下：
```
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);
```
![](https://support.huaweicloud.com/bestpractice-obs/public_sys-resources/note_3.0-zh-cn.png)
- 对象数据的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文件至本地为例，开启数据完整性校验的步骤如下：
1. 执行以下命令，检查待下载对象是否具有MD5信息。 
   ```
   obsutil stat obs://test-bucket/test.txt
   ```
   - 返回的对象基本信息中，包含MD5信息，如下图所示，执行[步骤2]。
     ![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001881875122.png "点击放大")
     
   
   - 不包含MD5信息，下载对象时无法进行完整性校验。
   
   
   
   
2. 执行以下命令，下载对象。
   
   ```
   obsutil cp obs://mytestbucket/test.txt D:\test.txt -vmd5
   ```
   - 对象下载成功且通过完整性校验，回显信息如下： ![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001882035038.png "点击放大")
     
   
   - 如果桶中对象没有MD5值，对象能够下载成功，但不会校验完整性，回显信息如下： ![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001927914353.png "点击放大")
     
   
   
   
   
 
#### 方式二：使用OBS Browser+校验下载对象的完整性
OBS Browser+默认关闭MD5校验，在OBS Browser+上启用MD5校验完整性并下载对象的步骤如下：
1. 登录OBS Browser+。
2. 单击客户端右上方的![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001927833953.png)，并选择"高级设置"。
3. 勾选"MD5校验"，如[图4]所示。
   
   图4配置MD5校验   
   ![](https://support.huaweicloud.com/bestpractice-obs/zh-cn_image_0000001881875158.png "点击放大")
   
   
4. 单击"确定"。
5. 选择待下载文件的桶，下载文件。 
   - 如果MD5校验成功，则文件下载成功。
   
   - 如果MD5校验失败，则文件下载失败，且在任务管理中提示失败原因：校验文件MD5失败。
   
   
   
   
 
#### 方式三：使用OBS SDK校验下载对象的完整性
OBS SDK对待下载对象的自定义元数据中的MD5值和下载到本地的对象的MD5值进行对比，通过对比结果判断下载对象的完整性。
此处以使用OBS Java SDK下载mytestbucket桶中一个名为test.txt的文本文件为例，下载过程使用MD5值校验数据完整性的示例代码如下：
```
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");
```
![](https://support.huaweicloud.com/bestpractice-obs/public_sys-resources/note_3.0-zh-cn.png)
在以上示例代码中，获取对象MD5值是在上传时设置的自定义元数据x-obs-meta-md5chksum，实际开发中需要根据自定义的元数据名称修改。
