2009-11-21 161 views
47

如果我在亚马逊S3上有现有文件,在不必下载文件的情况下获取md5sum的最简单方法是什么?如何获得亚马逊S3上文件的md5sum

谢谢

+0

ETag头是MD5,但不适用于多部分文件。以下是关于如何使用它的更多信息:http://stackoverflow.com/questions/6591047/etag-definition-changed-in-amazon-s3/31086810#31086810 – r03 2015-07-21 19:35:59

+0

考虑改变对这个问题的接受答案,因为人谁不读下面的评论和答案将被误导。 – JLo 2016-05-17 15:41:04

回答

35

嗯......我想你可以看看ETag头。 (在这种情况下,您可以使用HTTP HEAD方法而不是GET)。他们不太明确地说,但几乎可以肯定,Amazon使用MD5散列作为ETag。从PUT Object documentation(说不上来为什么他们不只是直截了当地说):

为了确保数据不被损坏,在网络上,使用Content-MD5标头。当您使用Content-MD5标头时,Amazon S3会根据提供的MD5值检查对象。如果它们不匹配,则Amazon S3会返回错误。此外,您可以在将对象放入Amazon S3时计算MD5,并将返回的Etag与计算出的MD5值进行比较。

此外,SOAP methods让您只请求元数据而不是数据本身。

+58

这个回答不再有效。 Etag也可以返回一些不同的东西,例如“08e1ef8708f6b9ba5f65596254f45111-1” – 2011-09-01 15:03:01

+1

如果我使用s3cmd上传,它会显式设置ETag为上传文件的(正确)MD5校验和。希望s3cmd也可以验证通过'put'上传的文件的校验和(http://aws.amazon.com/articles/1904)。 – 2012-06-07 20:01:47

+0

@AdamMonsen我不认为s3cmd会将ETag设置为MD5,因为在Web界面中,多部分上传文件的ETag仍然显示上面提到的内容中带有短划线的格式。算法如何计算ETag在[这个答案](http://stackoverflow.com/a/19304527/1132850)中解释。我想s3cmd设置一个自定义标题来保存md5总和。 – PiQuer 2014-08-19 10:03:06

21

对于分段上传,ETag似乎不是MD5(按照Gael Fraiteur的评论)。在这些情况下,它包含一个减号和一个数字的后缀。然而,即使是减号之前的位似乎也不是MD5,即使它与MD5的长度相同。后缀可能是上传的零件数量?

+3

此后缀似乎只在文件较大(大于5GB)时才会显示。通过检查我拥有的大量文件,看起来后缀代表上传的部分数量。但是,第一部分似乎没有与原始文件相同的md5散列。在计算这个散列时,亚马逊必须为每个部分折叠一些额外的数据。我想知道该算法,以便我可以检查一些文件。 – 2012-08-29 19:19:12

+2

哈希算法在这里描述:http://stackoverflow.com/questions/6591047/etag-definition-changed-in-amazon-s3 – Nakedible 2014-09-04 22:02:35

0

这对我的作品。 在PHP中,你使用这个可以比较本地文件Ë亚马逊文件的校验:



    // 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; 
     } 
    } 

3

对于任何人谁花时间来搜索周围找出为什么MD5不一样的ETag的S3。

ETag会根据数据卡盘计算并concat所有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; 
    } 
8

ETag AWS的文件说:

实体标记为对象的哈希值。 ETag仅反映对象内容的变化,而不反映其元数据。 ETag可能是也可能不是对象数据的MD5摘要。不论它是取决于对象是如何创建的,以及如何,如下所述加密:由PUT物件,POST对象或复制操作创建

  • 对象,或通过AWS管理控制台,并且是由SSE-S3或明文加密,具有ETags,它们是对象数据的MD5摘要。
  • 由PUT对象,POST对象或复制操作或通过AWS管理控制台创建并由SSE-C或SSE-KMS加密的对象具有不是其对象数据的MD5摘要的ETags。
  • 如果通过分段上载或部分复制操作创建对象,则无论采用何种加密方法,ETag都不是MD5摘要。

参考:http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html

0

这里是代码即可获得MD5哈希按照2017年

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); 
    } 
} 

注释代码是大多数人把它错误的将其更改为十六进制

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 
}