上传到Amazon S3的小于5GB的文件有一个ETag,它只是文件的MD5哈希,这使您可以轻松检查本地文件是否与您在S3上的文件相同.
但是如果你的文件大于5GB,那么亚马逊会以不同的方式计算ETag.
例如,我在380个零件中进行了5,970,150,664字节文件的分段上传.现在S3显示它有一个ETag 6bcf86bed8807b8e78f0fc6e0a53079d-380
.我的本地文件有一个md5哈希值702242d3703818ddefe6bf7da2bed757
.我认为短划线后面的数字是分段上传中的部件数量.
我还怀疑新的ETag(在破折号之前)仍然是MD5哈希,但是在某种程度上从分段上传中包含了一些元数据.
有谁知道如何使用与Amazon S3相同的算法计算ETag?
Eme*_*gia 76
刚刚验证了一个.向亚马逊致敬,让它足够简单,可以猜到.
假设您上传了一个14MB的文件,您的零件尺寸为5MB.计算与每个部分对应的3个MD5校验和,即前5MB,第二个5MB和最后4MB的校验和.然后取其连接的校验和.由于MD5校验和是二进制数据的十六进制表示,因此请确保采用解码二进制连接的MD5,而不是ASCII或UTF-8编码级联.完成后,添加连字符和部件数量以获得ETag.
以下是从控制台在Mac OS X上执行此操作的命令:
$ dd bs=1m count=5 skip=0 if=someFile | md5 >>checksums.txt
5+0 records in
5+0 records out
5242880 bytes transferred in 0.019611 secs (267345449 bytes/sec)
$ dd bs=1m count=5 skip=5 if=someFile | md5 >>checksums.txt
5+0 records in
5+0 records out
5242880 bytes transferred in 0.019182 secs (273323380 bytes/sec)
$ dd bs=1m count=5 skip=10 if=someFile | md5 >>checksums.txt
2+1 records in
2+1 records out
2599812 bytes transferred in 0.011112 secs (233964895 bytes/sec)
Run Code Online (Sandbox Code Playgroud)
此时所有校验和都在checksums.txt
.要连接它们并解码十六进制并获得该批次的MD5校验和,只需使用
$ xxd -r -p checksums.txt | md5
Run Code Online (Sandbox Code Playgroud)
现在附加"-3"来获得ETag,因为有3个部分.
值得注意的是,md5
在Mac OS X上只写出校验和,但md5sum
在Linux上也输出文件名.你需要删除它,但我确信有一些选项只能输出校验和.您不必担心空白原因xxd
会忽略它.
注意:如果你上传的AWS-CLI通过aws s3 cp
,那么你很可能有8MB CHUNKSIZE.根据文档,这是默认的.
更新:我在https://github.com/Teachnova/s3md5上被告知有关此实现,这在OS X上不起作用.这是我用OS X编写的工作脚本的Gist .
相同的算法,java版本:( BaseEncoding,Hasher,Hashing等来自番石榴库
/**
* Generate checksum for object came from multipart upload</p>
* </p>
* AWS S3 spec: Entity tag that identifies the newly created object's data. Objects with different object data will have different entity tags. The entity tag is an opaque string. The entity tag may or may not be an MD5 digest of the object data. If the entity tag is not an MD5 digest of the object data, it will contain one or more nonhexadecimal characters and/or will consist of less than 32 or more than 32 hexadecimal digits.</p>
* Algorithm follows AWS S3 implementation: https://github.com/Teachnova/s3md5</p>
*/
private static String calculateChecksumForMultipartUpload(List<String> md5s) {
StringBuilder stringBuilder = new StringBuilder();
for (String md5:md5s) {
stringBuilder.append(md5);
}
String hex = stringBuilder.toString();
byte raw[] = BaseEncoding.base16().decode(hex.toUpperCase());
Hasher hasher = Hashing.md5().newHasher();
hasher.putBytes(raw);
String digest = hasher.hash().toString();
return digest + "-" + md5s.size();
}
Run Code Online (Sandbox Code Playgroud)
小智 8
不确定它是否可以提供帮助:
目前,我们正在做一个丑陋的(但到目前为止,很有用)黑客以修复这些错误的ETag在多载的文件,其中包括对应用变化桶里的文件; 触发从Amazon重新计算的md5,将ETag更改为与实际的md5签名匹配.
在我们的情况下:
文件:bucket/Foo.mpg.gpg
我们不知道算法,但由于我们可以"修复"ETag,我们也不需要担心它.
根据这里的答案,我编写了一个Python实现,可以正确计算多部分和单部分文件ETag.
def calculate_s3_etag(file_path, chunk_size=8 * 1024 * 1024):
md5s = []
with open(file_path, 'rb') as fp:
while True:
data = fp.read(chunk_size)
if not data:
break
md5s.append(hashlib.md5(data))
if len(md5s) == 1:
return '"{}"'.format(md5s[0].hexdigest())
digests = b''.join(m.digest() for m in md5s)
digests_md5 = hashlib.md5(digests)
return '"{}-{}"'.format(digests_md5.hexdigest(), len(md5s))
Run Code Online (Sandbox Code Playgroud)
默认的chunk_size是官方aws cli
工具使用的8 MB ,它为2+块进行分段上传.它应该在Python 2和3下工作.
在上面的答案中,有人问是否有办法为大于5G的文件获取md5。
为了获得MD5值(对于大于5G的文件),我可以给出的答案是将其手动添加到元数据中,或者使用程序进行上传以添加信息。
例如,我使用s3cmd上传文件,并添加了以下元数据。
$ aws s3api head-object --bucket xxxxxxx --key noarch/epel-release-6-8.noarch.rpm
{
"AcceptRanges": "bytes",
"ContentType": "binary/octet-stream",
"LastModified": "Sat, 19 Sep 2015 03:27:25 GMT",
"ContentLength": 14540,
"ETag": "\"2cd0ae668a585a14e07c2ea4f264d79b\"",
"Metadata": {
"s3cmd-attrs": "uid:502/gname:staff/uname:xxxxxx/gid:20/mode:33188/mtime:1352129496/atime:1441758431/md5:2cd0ae668a585a14e07c2ea4f264d79b/ctime:1441385182"
}
}
Run Code Online (Sandbox Code Playgroud)
这不是使用ETag的直接解决方案,而是一种可以访问所需元数据(MD5)的方式。如果有人上传没有元数据的文件,它仍然会失败。
根据AWS文档,ETag既不是分段上传的MD5哈希,也不是加密对象的MD5哈希:http : //docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html
由PUT对象,POST对象或复制操作创建的对象,或者通过AWS管理控制台创建的,并由SSE-S3或纯文本加密的对象,其ETag是其对象数据的MD5摘要。
由PUT对象,POST对象或复制操作创建的对象,或者通过AWS管理控制台创建并由SSE-C或SSE-KMS加密的对象,其ETag不是其对象数据的MD5摘要。
如果通过“分段上传”或“部分复制”操作创建了对象,则无论采用哪种加密方法,ETag都不是MD5摘要。
Node.js 实现 -
const fs = require('fs');
const crypto = require('crypto');
const chunk = 1024 * 1024 * 5; // 5MB
const md5 = data => crypto.createHash('md5').update(data).digest('hex');
const getEtagOfFile = (filePath) => {
const stream = fs.readFileSync(filePath);
if (stream.length <= chunk) {
return md5(stream);
}
const md5Chunks = [];
const chunksNumber = Math.ceil(stream.length / chunk);
for (let i = 0; i < chunksNumber; i++) {
const chunkStream = stream.slice(i * chunk, (i + 1) * chunk);
md5Chunks.push(md5(chunkStream));
}
return `${md5(Buffer.from(md5Chunks.join(''), 'hex'))}-${chunksNumber}`;
};
Run Code Online (Sandbox Code Playgroud)
这是这个疯狂的 AWS 挑战难题中的另一部分。
FWIW,这个答案假设您已经知道如何计算“MD5 部分的 MD5”,并且可以从此处提供的所有其他答案中重建您的 AWS 多部分 ETag。
这个答案解决的是必须“猜测”或以其他方式“确定”原始上传部分大小的烦恼。
我们使用几种不同的工具上传到 S3,它们似乎都有不同的上传部分大小,所以“猜测”真的不是一个选择。此外,我们有很多文件在历史上上传时部分大小似乎不同。此外,使用内部服务器副本强制创建 MD5 类型 ETag 的老技巧也不再有效,因为 AWS 已将其内部服务器副本更改为也使用多部分(只是具有相当大的部分大小)。
那么...你怎么能算出物体的零件尺寸?
好吧,如果您首先发出一个 head_object 请求并检测到 ETag 是一个多部分类型的 ETag(最后包含一个“-<partcount>”),那么您可以发出另一个 head_object 请求,但附加一个 part_number 属性1(第一部分)。这个后续的 head_object 请求将返回第一部分的 content_length。中提琴...现在您知道所使用的部分大小,您可以使用该大小重新创建本地 ETag,该 ETag 应与上传对象时创建的原始上传 S3 ETag 相匹配。
此外,如果您想要准确(也许某些分段上传要使用可变的部分大小),那么您可以继续调用指定每个 part_number 的 head_object 请求,并根据返回的部分 content_length 计算每个部分的 MD5。
希望有帮助...
归档时间: |
|
查看次数: |
32343 次 |
最近记录: |