You need to enable JavaScript to run this app.
导航

使用 ETag 验证简单上传场景下 TOS 对象的完整性

最近更新时间2024.01.03 11:00:03

首次发布时间2024.01.03 11:00:03

本文介绍使用 ETag 验证简单上传对象完整性的过程。

前言

使用TOS进行数据上传或下载的过程中,可能会因为公网传输过程中在TOS外部的网络劫持、数据缓存等原因,导致数据不一致等问题。TOS提供了多种数据一致性相关的特性,您可以利用这些特性确保数据上传或下载的一致性。在TOS场景中,数据一致性校验分为上传对象一致性校验以及下载对象一致性校验,。

本实验介绍使用Etag验证简单上传场景下的数据完整性。

关于实验
  • 预计部署时间:30分钟
  • 级别:初级
  • 相关产品:TOS
  • 受众: 通用
实验说明
  • 点击此链接登录控制台。

  • 如果您还没有账户,请点击此链接注册账户。

实验步骤

一、完整性效验原理

将对象上传到TOS时,它们可以作为单个对象上传,也可以通过分片进行上传。

当对象使用简单上传时,该对象的 Etag 即为整个对象的MD5摘要,根据上传完成后响应的Etag跟本地文件的Etag进行对比即可;对于已经上传完成,后续要对其进行完整性效验的场景,可以使用HeadObject来获取对象Etag,然后进行对比。

当对象使用分片上传时,该对象的 ETag 不是整个对象的 MD5 摘要。TOS 会在上传时计算每个分片的 MD5 摘要。MD5 摘要用于确定最终对象的 ETag。TOS 将 MD5 摘要的字节串联在一起,然后计算这些串联值的 MD5 摘要。创建 ETag 的最后一步为在末尾添加一个短横线和分片总数。

示例,考虑使用 ETag 为 C9A5A6878D97B48CC965C1E41859F034-14 的分片上传的对象上传。在本例中,C9A5A6878D97B48CC965C1E41859F034 是串联在一起的所有摘要的 MD5 摘要。-14 表示有 14 个分片与此对象的分片上传相关联。

您可以使用 GetObject 或 HeadObject 操作并指定与单个分片对齐的分片编号或字节范围,来获取单个分片的校验和。借助此方法,您可以使用该校验和验证单个分片,而无需等待所有分片都上传完毕后再验证数据完整性。使用此方法时,还可以仅请求未通过完整性测试的单个分片。

由于 TOS计算分片对象的校验和的方式,复制对象时,对象的校验和值可能会发生变化。如果您使用的是SDK 或 REST API,并且调用 CopyObject,TOS可以复制大小不超过 CopyObject API 操作限制的任何对象。无论对象是在单个请求中上传还是作为分片上传的一部分上传,TOS都可以作为单个操作执行此复制。使用 copy 命令,对象的校验和是完整对象的直接校验和。如果对象最初是使用分片上传进行上传的,那么即使数据没有变化,校验和值也会发生变化。

二、代码实现

package main

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/volcengine/ve-tos-golang-sdk/v2/tos"
	"io/ioutil"
	"os"
	"strings"
)

func log(msg string) {
	fmt.Println(msg + "\n")
}

// 获取文件的 md5
func Md5Value(bytes []byte) (md5s string) {
	h := md5.New()
	h.Write(bytes)
	md5s = hex.EncodeToString(h.Sum(nil))
	return
}

// 获取文件的 md5 值
func GetFileMd5Hash(fileBytes []byte) string {
	hash := Md5Value(fileBytes)
	log(fmt.Sprintf("get file hash: %v", hash))
	return hash
}

func main() {

	// 先计算文件的MD5
	fileName := "test"
	
	path := fmt.Sprintf("/Users/xxxx/%s", fileName)
	
	localfile, err := os.Open(path)
	
	if err != nil {
		panic(err)
	}
	defer localfile.Close()
	
	all, err := ioutil.ReadAll(localfile)

	localMD5Values := GetFileMd5Hash(all)

	// 上传对象并获取返回的Etag
	var (
		accessKey = "xxxxx"
		secretKey = "xxxxx"
		endpoint  = "tos-cn-beijing.volces.com"
		region    = "cn-beijing"
	)

	client, errClient := tos.NewClientV2(endpoint, tos.WithRegion(region), tos.WithCredentials(tos.NewStaticCredentials(accessKey, secretKey)))

	if errClient != nil {
		panic(err)
	}

	file, _ := os.Open("/Users/xxxx/test")

	inputstruct := tos.PutObjectBasicInput{
		Bucket: "xxxx",
		Key:    "xxxx",
	}

	get, err := client.PutObjectV2(context.Background(), &tos.PutObjectV2Input{
		inputstruct,
		file,
	})

	if err != nil {
		fmt.Println(err)
	}

	EtagValue := strings.Trim(get.ETag, "\"")

	fmt.Println(EtagValue)

	if EtagValue == localMD5Values {
		fmt.Println("效验成功")
	} else {
		fmt.Println("效验失败")
		// 效验失败可以添加失败处理逻辑
	}

}

参考文档:
[1] TOS Go SDK