Wenn ich bereits Dateien auf Amazon S3 habe, wie kann ich die md5sum am einfachsten abrufen, ohne die Dateien herunterladen zu müssen?
Vielen Dank
Wenn ich bereits Dateien auf Amazon S3 habe, wie kann ich die md5sum am einfachsten abrufen, ohne die Dateien herunterladen zu müssen?
Vielen Dank
Antworten:
Die Dokumentation von AWS ETag
lautet:
Das Entity-Tag ist ein Hash des Objekts. Das ETag spiegelt nur Änderungen am Inhalt eines Objekts wider, nicht dessen Metadaten. Das ETag kann ein MD5-Digest der Objektdaten sein oder nicht. Ob dies der Fall ist oder nicht, hängt davon ab, wie das Objekt erstellt und wie unten beschrieben verschlüsselt wurde:
- Objekte, die vom PUT-Objekt-, POST-Objekt- oder Kopiervorgang oder über die AWS Management Console erstellt und mit SSE-S3 oder Klartext verschlüsselt wurden, verfügen über ETags, die einen MD5-Digest ihrer Objektdaten darstellen.
- Objekte, die durch den Vorgang PUT-Objekt, POST-Objekt oder Kopieren oder über die AWS Management Console erstellt und von SSE-C oder SSE-KMS verschlüsselt wurden, verfügen über ETags, die keine MD5-Zusammenfassung ihrer Objektdaten darstellen.
- Wenn ein Objekt entweder durch das Hochladen mehrerer Teile oder durch das Kopieren von Teilen erstellt wird, ist das ETag unabhängig von der Verschlüsselungsmethode kein MD5-Digest.
Referenz: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html
ETag scheint kein MD5 für mehrteilige Uploads zu sein (gemäß dem Kommentar von Gael Fraiteur). In diesen Fällen enthält es ein Suffix von Minus und eine Zahl. Selbst das Bit vor dem Minus scheint jedoch nicht das MD5 zu sein, obwohl es die gleiche Länge wie ein MD5 hat. Möglicherweise ist das Suffix die Anzahl der hochgeladenen Teile?
aws s3 cp ...
Im Folgenden kann ich die Prüfsumme für lokale Dateien mit s3 etag vergleichen. Ich habe Python benutzt
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
your_key
gleiche wie das filename
?
etag_checksum
Funktion ist ein Problem mit Dateien aufgetreten, die kleiner als diese sind chunkt_size
. Ein einfacher Test der Dateigröße ( os.path.getsize(filename) < chunk_size
) hat dies behoben, falls auch jemand anderes dieses Problem hat. In diesem Fall lautet der Hashhashlib.md5(f.read())
Für alle, die Zeit damit verbringen, sich umzuschauen, um herauszufinden, warum der md5 nicht mit ETag in S3 identisch ist.
ETag berechnet gegen das Einspannen von Daten und konzentriert alle md5hash, um md5-Hash erneut zu erstellen und die Anzahl der Chunks am Ende beizubehalten.
Hier ist die C # -Version zum Generieren von Hash
string etag = HashOf("file.txt",8);
Quellcode
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;
}
Dies ist eine sehr alte Frage, aber es fiel mir schwer, die folgenden Informationen zu finden, und dies ist einer der ersten Orte, die ich finden konnte. Deshalb wollte ich sie detailliert beschreiben, falls jemand sie benötigt.
ETag ist ein MD5. Für die hochgeladenen Multipart-Dateien wird der MD5 jedoch aus der Verkettung der MD5s jedes hochgeladenen Teils berechnet. Sie müssen den MD5 also nicht auf dem Server berechnen. Holen Sie sich einfach den ETag und es ist alles.
Wie @EmersonFarrugia in dieser Antwort sagte :
Angenommen, Sie haben eine 14-MB-Datei hochgeladen und Ihre Teilegröße beträgt 5 MB. Berechnen Sie 3 MD5-Prüfsummen für jedes Teil, dh die Prüfsumme der ersten 5 MB, der zweiten 5 MB und der letzten 4 MB. Nehmen Sie dann die Prüfsumme ihrer Verkettung. Da MD5-Prüfsummen hexadezimale Darstellungen von Binärdaten sind, stellen Sie sicher, dass Sie das MD5 der decodierten binären Verkettung und nicht der ASCII- oder UTF-8-codierten Verkettung verwenden. Wenn dies erledigt ist, fügen Sie einen Bindestrich und die Anzahl der Teile hinzu, um das ETag zu erhalten.
Das einzige andere, was Sie brauchen, ist das ETag und die Upload-Teilegröße. Das ETag hat jedoch das Suffix -NumberOfParts. So können Sie die Größe durch das Suffix teilen und die Teilegröße erhalten. 5 MB ist die minimale Teilegröße und der Standardwert. Die Teilegröße muss eine Ganzzahl sein, damit Sie nicht 7,25 MB pro Teilegröße erhalten können. Es sollte also einfach sein, Informationen zur Teilegröße zu erhalten.
Hier ist ein Skript, um dies in osx zu machen, mit einer Linux-Version in Kommentaren: https://gist.github.com/emersonf/7413337
Ich werde beide Skripte hier belassen, falls die obige Seite in Zukunft nicht mehr zugänglich ist:
Linux-Version:
#!/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-Version:
#!/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
Ich habe festgestellt, dass s3cmd eine Option --list-md5 hat, die mit dem Befehl ls verwendet werden kann, z
s3cmd ls --list-md5 s3://bucket_of_mine/
Hoffe das hilft.
Ich habe jets3t und die Verwaltungskonsole mit der MD5sum der hochgeladenen Dateien verglichen, und ETag scheint MD5sum zu entsprechen. Sie können die Eigenschaften der Datei einfach in der AWS-Verwaltungskonsole anzeigen:
Am einfachsten ist es, die Prüfsumme selbst als Metadaten festzulegen, bevor Sie diese Dateien in Ihren Bucket hochladen:
ObjectMetadata md = new ObjectMetadata();
md.setContentMD5("foobar");
PutObjectRequest req = new PutObjectRequest(BUCKET, KEY, new File("/path/to/file")).withMetadata(md);
tm.upload(req).waitForUploadResult();
Jetzt können Sie auf diese Metadaten zugreifen, ohne die Datei herunterladen zu müssen:
ObjectMetadata md2 = s3Client.getObjectMetadata(BUCKET, KEY);
System.out.println(md.getContentMD5());
Das funktioniert bei mir. In PHP können Sie die Prüfsumme zwischen lokaler Datei und Amazon-Datei folgendermaßen vergleichen:
// 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;
}
}
Hier ist der Code zum Abrufen des S3 ETag für ein Objekt in PowerShell, das aus c # konvertiert wurde.
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
}
Hier ist der Code, um MD5-Hash ab 2017 zu erhalten
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);
}
}
Der kommentierte Code ist der Punkt, an dem die meisten Leute etwas falsch machen, wenn sie ihn in hexadezimal ändern