如果我在亚马逊S3上有现有文件,那么在不下载文件的情况下获取md5sum的最简单方法是什么?
由于
答案 0 :(得分:37)
嗯......我想你可以看一下ETag标题。 (在这种情况下,你可以使用HTTP HEAD方法而不是GET。)他们并没有明确地说明,但几乎可以肯定的是亚马逊使用MD5哈希进行ETag。来自PUT Object documentation(不知道他们为什么不直接说出来):
要确保网络上的数据不被破坏,请使用Content-MD5标头。使用Content-MD5标头时,Amazon S3会根据提供的MD5值检查对象。如果它们不匹配,Amazon S3将返回错误。此外,您可以在将对象放入Amazon S3时计算MD5,并将返回的Etag与计算的MD5值进行比较。
此外,SOAP methods只允许您请求元数据,而不是数据本身。
答案 1 :(得分:22)
对于分段上传,ETag似乎不是MD5(根据Gael Fraiteur的评论)。在这些情况下,它包含一个减号和数字的后缀。然而,即使它与MD5的长度相同,即使在减号之前的位似乎也不是MD5。可能后缀是上传的部件数量?
答案 2 :(得分:18)
AWS的ETag
文档说:
实体标签是对象的哈希值。 ETag仅反映对象内容的更改,而不反映其元数据。 ETag可能是也可能不是对象数据的MD5摘要。它是否取决于对象的创建方式以及如何加密,如下所述:
- 由PUT对象,POST对象或复制操作或通过AWS管理控制台创建并由SSE-S3或纯文本加密的对象具有ETag,这些ETag是其对象数据的MD5摘要。
- 由PUT对象,POST对象或复制操作或通过AWS管理控制台创建并由SSE-C或SSE-KMS加密的对象具有不是其对象数据的MD5摘要的ETag。 / LI>
- 如果通过分段上传或零件复制操作创建对象,则无论加密方法如何,ETag都不是MD5摘要。
参考:http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html
答案 3 :(得分:5)
任何花时间搜索周围的人都会发现为什么md5与S3中的ETag不一样。
ETag将计算数据的chuck并将所有md5hash连接起来再次生成md5哈希并保留最后的块数。
这是C#版本生成哈希
string etag = HashOf("file.txt",8);
源代码
private string HashOf(string filename,int chunkSizeInMb)
{
string returnMD5 = string.Empty;
int chunkSize = chunkSizeInMb * 1024 * 1024;
using (var crypto = new MD5CryptoServiceProvider())
{
int hashLength = crypto.HashSize/8;
using (var stream = File.OpenRead(filename))
{
if (stream.Length > chunkSize)
{
int chunkCount = (int)Math.Ceiling((double)stream.Length/(double)chunkSize);
byte[] hash = new byte[chunkCount*hashLength];
Stream hashStream = new MemoryStream(hash);
long nByteLeftToRead = stream.Length;
while (nByteLeftToRead > 0)
{
int nByteCurrentRead = (int)Math.Min(nByteLeftToRead, chunkSize);
byte[] buffer = new byte[nByteCurrentRead];
nByteLeftToRead -= stream.Read(buffer, 0, nByteCurrentRead);
byte[] tmpHash = crypto.ComputeHash(buffer);
hashStream.Write(tmpHash, 0, hashLength);
}
returnMD5 = BitConverter.ToString(crypto.ComputeHash(hash)).Replace("-", string.Empty).ToLower()+"-"+ chunkCount;
}
else {
returnMD5 = BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", string.Empty).ToLower();
}
stream.Close();
}
}
return returnMD5;
}
答案 4 :(得分:5)
这是一个非常老的问题,但是我很难找到下面的信息,这是我可以找到的第一个地方,因此我想详细介绍一下,以防有人需要。
ETag是MD5。但是对于分段上传的文件,MD5是根据每个上传部分的MD5的串联来计算的。 因此,您无需在服务器中计算MD5。只需获取ETag即可。
正如@EmersonFarrugia在this中所说的那样:
假设您上传了一个14MB的文件,部件大小为5MB。计算每个部分对应的3个MD5校验和,即前5MB,后5MB和后4MB的校验和。然后取其串联的校验和。由于MD5校验和是二进制数据的十六进制表示,因此请确保您使用的是解码后的二进制级联的MD5,而不是ASCII或UTF-8编码级联的MD5。完成后,添加连字符和零件数量以获取ETag。
因此,您唯一需要做的其他事情就是ETag和上传部件的大小。但是ETag具有-NumberOfParts后缀。因此,您可以将大小除以后缀并得到零件大小。 5Mb是最小零件尺寸和默认值。零件大小必须为整数,因此每个零件大小都无法获得7,25Mb之类的东西。因此,获取零件尺寸信息应该很容易。
以下是在osx中实现此功能的脚本,并在注释中带有Linux版本:https://gist.github.com/emersonf/7413337
如果以后不再可以访问上面的页面,我将在此处保留两个脚本:
Linux版本:
#!/bin/bash
set -euo pipefail
if [ $# -ne 2 ]; then
echo "Usage: $0 file partSizeInMb";
exit 0;
fi
file=$1
if [ ! -f "$file" ]; then
echo "Error: $file not found."
exit 1;
fi
partSizeInMb=$2
fileSizeInMb=$(du -m "$file" | cut -f 1)
parts=$((fileSizeInMb / partSizeInMb))
if [[ $((fileSizeInMb % partSizeInMb)) -gt 0 ]]; then
parts=$((parts + 1));
fi
checksumFile=$(mktemp -t s3md5.XXXXXXXXXXXXX)
for (( part=0; part<$parts; part++ ))
do
skip=$((partSizeInMb * part))
$(dd bs=1M count=$partSizeInMb skip=$skip if="$file" 2> /dev/null | md5sum >> $checksumFile)
done
etag=$(echo $(xxd -r -p $checksumFile | md5sum)-$parts | sed 's/ --/-/')
echo -e "${1}\t${etag}"
rm $checksumFile
OSX版本:
#!/bin/bash
if [ $# -ne 2 ]; then
echo "Usage: $0 file partSizeInMb";
exit 0;
fi
file=$1
if [ ! -f "$file" ]; then
echo "Error: $file not found."
exit 1;
fi
partSizeInMb=$2
fileSizeInMb=$(du -m "$file" | cut -f 1)
parts=$((fileSizeInMb / partSizeInMb))
if [[ $((fileSizeInMb % partSizeInMb)) -gt 0 ]]; then
parts=$((parts + 1));
fi
checksumFile=$(mktemp -t s3md5)
for (( part=0; part<$parts; part++ ))
do
skip=$((partSizeInMb * part))
$(dd bs=1m count=$partSizeInMb skip=$skip if="$file" 2>/dev/null | md5 >>$checksumFile)
done
echo $(xxd -r -p $checksumFile | md5)-$parts
rm $checksumFile
答案 5 :(得分:2)
下面这对我来说是比较本地文件校验和与s3 etag的工作。 我用Python
def md5_checksum(filename):
m = hashlib.md5()
with open(filename, 'rb') as f:
for data in iter(lambda: f.read(1024 * 1024), b''):
m.update(data)
return m.hexdigest()
def etag_checksum(filename, chunk_size=8 * 1024 * 1024):
md5s = []
with open(filename, 'rb') as f:
for data in iter(lambda: f.read(chunk_size), b''):
md5s.append(hashlib.md5(data).digest())
m = hashlib.md5(b"".join(md5s))
print('{}-{}'.format(m.hexdigest(), len(md5s)))
return '{}-{}'.format(m.hexdigest(), len(md5s))
def etag_compare(filename, etag):
et = etag[1:-1] # strip quotes
print('et',et)
if '-' in et and et == etag_checksum(filename):
return True
if '-' not in et and et == md5_checksum(filename):
return True
return False
def main():
session = boto3.Session(
aws_access_key_id=s3_accesskey,
aws_secret_access_key=s3_secret
)
s3 = session.client('s3')
obj_dict = s3.get_object(Bucket=bucket_name, Key=your_key)
etag = (obj_dict['ETag'])
print('etag', etag)
validation = etag_compare(filename,etag)
print(validation)
etag_checksum(filename, chunk_size=8 * 1024 * 1024)
return validation
答案 6 :(得分:1)
我已经针对上传的文件'MD5sum交叉检查了jets3t和管理控制台,ETag似乎等于MD5sum。您只需在AWS管理控制台中查看该文件的属性:
答案 7 :(得分:1)
我找到了这个答案,但是(对我来说)并没有提供一个简单的答案。然后我发现s3cmd具有--list-md5选项,可与ls命令一起使用,例如
s3cmd ls --list-md5 s3://bucket_of_mine/
希望这会有所帮助。
答案 8 :(得分:0)
这对我有用。 在PHP中,您可以使用以下方法比较本地文件e amazon文件之间的校验和:
// get localfile md5
$checksum_local_file = md5_file ( '/home/file' );
// compare checksum between localfile and s3file
public function compareChecksumFile($file_s3, $checksum_local_file) {
$Connection = new AmazonS3 ();
$bucket = amazon_bucket;
$header = $Connection->get_object_headers( $bucket, $file_s3 );
// get header
if (empty ( $header ) || ! is_object ( $header )) {
throw new RuntimeException('checksum error');
}
$head = $header->header;
if (empty ( $head ) || !is_array($head)) {
throw new RuntimeException('checksum error');
}
// get etag (md5 amazon)
$etag = $head['etag'];
if (empty ( $etag )) {
throw new RuntimeException('checksum error');
}
// remove quotes
$checksumS3 = str_replace('"', '', $etag);
// compare md5
if ($checksum_local_file === $checksumS3) {
return TRUE;
} else {
return FALSE;
}
}
答案 9 :(得分:0)
以下是从c#转换为PowerShell中对象的S3 ETag的代码。
function Get-ETag {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Path,
[Parameter(Mandatory=$true)]
[int]$ChunkSizeInMb
)
$returnMD5 = [string]::Empty
[int]$chunkSize = $ChunkSizeInMb * [Math]::Pow(2, 20)
$crypto = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
[int]$hashLength = $crypto.HashSize / 8
$stream = [System.IO.File]::OpenRead($Path)
if($stream.Length -gt $chunkSize) {
$chunkCount = [int][Math]::Ceiling([double]$stream.Length / [double]$chunkSize)
[byte[]]$hash = New-Object byte[]($chunkCount * $hashLength)
$hashStream = New-Object System.IO.MemoryStream(,$hash)
[long]$numBytesLeftToRead = $stream.Length
while($numBytesLeftToRead -gt 0) {
$numBytesCurrentRead = [int][Math]::Min($numBytesLeftToRead, $chunkSize)
$buffer = New-Object byte[] $numBytesCurrentRead
$numBytesLeftToRead -= $stream.Read($buffer, 0, $numBytesCurrentRead)
$tmpHash = $crypto.ComputeHash($buffer)
$hashStream.Write($tmpHash, 0, $hashLength)
}
$returnMD5 = [System.BitConverter]::ToString($crypto.ComputeHash($hash)).Replace("-", "").ToLower() + "-" + $chunkCount
}
else {
$returnMD5 = [System.BitConverter]::ToString($crypto.ComputeHash($stream)).Replace("-", "").ToLower()
}
$stream.Close()
$returnMD5
}
答案 10 :(得分:0)
最简单的方法是,在将这些文件上传到存储桶之前,自己将校验和设置为元数据:
ObjectMetadata md = new ObjectMetadata();
md.setContentMD5("foobar");
PutObjectRequest req = new PutObjectRequest(BUCKET, KEY, new File("/path/to/file")).withMetadata(md);
tm.upload(req).waitForUploadResult();
现在,您无需下载文件即可访问这些元数据:
ObjectMetadata md2 = s3Client.getObjectMetadata(BUCKET, KEY);
System.out.println(md.getContentMD5());
答案 11 :(得分:-2)
以下是根据2017年获得MD5哈希的代码
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
public class GenerateMD5 {
public static void main(String args[]) throws Exception{
String s = "<CORSConfiguration> <CORSRule> <AllowedOrigin>http://www.example.com</AllowedOrigin> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <AllowedHeader>*</AllowedHeader> <MaxAgeSeconds>3000</MaxAgeSeconds> </CORSRule> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedHeader>*</AllowedHeader> <MaxAgeSeconds>3000</MaxAgeSeconds> </CORSRule> </CORSConfiguration>";
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(s.getBytes());
byte[] digest = md.digest();
StringBuffer sb = new StringBuffer();
/*for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}*/
System.out.println(sb.toString());
StringBuffer sbi = new StringBuffer();
byte [] bytes = Base64.encodeBase64(digest);
String finalString = new String(bytes);
System.out.println(finalString);
}
}
评论代码是大多数人错误地将其更改为十六进制
的地方