Auf Amazon S3 hochgeladene Dateien, die kleiner als 5 GB sind, haben ein ETag, das einfach der MD5-Hash der Datei ist. Auf diese Weise können Sie leicht überprüfen, ob Ihre lokalen Dateien mit denen in S3 übereinstimmen.
Wenn Ihre Datei jedoch größer als 5 GB ist, berechnet Amazon das ETag anders.
Zum Beispiel habe ich einen mehrteiligen Upload einer 5.970.150.664-Byte-Datei in 380 Teilen durchgeführt. Jetzt zeigt S3, dass es einen ETag von hat 6bcf86bed8807b8e78f0fc6e0a53079d-380
. Meine lokale Datei hat einen MD5-Hash von 702242d3703818ddefe6bf7da2bed757
. Ich denke, die Zahl nach dem Bindestrich ist die Anzahl der Teile im mehrteiligen Upload.
Ich vermute auch, dass das neue ETag (vor dem Bindestrich) immer noch ein MD5-Hash ist, aber mit einigen Metadaten, die auf dem Weg vom mehrteiligen Upload irgendwie enthalten sind.
Weiß jemand, wie man das ETag mit demselben Algorithmus wie Amazon S3 berechnet?
Antworten:
Gerade verifiziert. Hut ab vor Amazon, weil es so einfach ist, dass man es erraten kann.
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.
Hier sind die Befehle, um dies unter Mac OS X über die Konsole zu tun:
Zu diesem Zeitpunkt sind alle Prüfsummen in
checksums.txt
. Um sie zu verketten und das Hex zu dekodieren und die MD5-Prüfsumme des Loses zu erhalten, verwenden Sie einfachUnd jetzt "-3" anhängen, um den ETag zu erhalten, da es 3 Teile gab.
Es ist erwähnenswert, dass
md5
unter Mac OS X nur die Prüfsumme geschrieben wird,md5sum
unter Linux jedoch auch der Dateiname ausgegeben wird. Sie müssen das entfernen, aber ich bin sicher, dass es eine Option gibt, nur die Prüfsummen auszugeben. Sie müssen sich keine Gedanken über Leerzeichen machenxxd
, da diese ignoriert werden.Hinweis : Wenn Sie mit aws-cli über hochgeladen
aws s3 cp
haben, haben Sie höchstwahrscheinlich eine 8-MB-Blockgröße. Laut den Dokumenten ist dies die Standardeinstellung.Update : Unter https://github.com/Teachnova/s3md5 wurde mir über eine Implementierung informiert , die unter OS X nicht funktioniert. Hier ist eine Zusammenfassung, die ich mit einem funktionierenden Skript für OS X geschrieben habe .
quelle
Basierend auf den Antworten hier habe ich eine Python-Implementierung geschrieben, die sowohl mehrteilige als auch einteilige Datei-ETags korrekt berechnet.
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(hashlib.md5().hexdigest()) 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))
Die Standardgröße von chunk_size ist 8 MB, die vom offiziellen
aws cli
Tool verwendet wird, und es werden mehrteilige Uploads für 2+ Chunks durchgeführt. Es sollte sowohl unter Python 2 als auch unter Python 3 funktionieren.quelle
Bash-Implementierung
Python-Implementierung
Der Algorithmus ist buchstäblich (aus der Readme-Datei in der Python-Implementierung kopiert):
quelle
Nicht sicher, ob es helfen kann:
Wir machen derzeit einen hässlichen (aber bisher nützlichen) Hack, um diese falschen ETags in mehrteilig hochgeladenen Dateien zu beheben. Dies besteht darin, eine Änderung an der Datei im Bucket vorzunehmen . Dies löst eine MD5-Neuberechnung von Amazon aus, bei der das ETag so geändert wird, dass es mit der tatsächlichen MD5-Signatur übereinstimmt.
In unserem Fall:
Datei: Bucket / Foo.mpg.gpg
Wir kennen den Algorithmus nicht, aber da wir den ETag "reparieren" können, müssen wir uns auch keine Sorgen machen.
quelle
Gleicher Algorithmus, Java-Version: (BaseEncoding, Hasher, Hashing usw. stammen aus der Guavenbibliothek
/** * 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(); }
quelle
DigestUtils.md5Hex(raw)
vonapache-commons
anstelle von Guava Hasher verwenden.In einer obigen Antwort fragte jemand, ob es eine Möglichkeit gibt, den md5 für Dateien größer als 5G zu erhalten.
Eine Antwort, die ich geben könnte, um den MD5-Wert (für Dateien größer als 5 G) zu erhalten, wäre, ihn entweder manuell zu den Metadaten hinzuzufügen oder ein Programm zu verwenden, um Ihre Uploads durchzuführen, die die Informationen hinzufügen.
Zum Beispiel habe ich s3cmd verwendet, um eine Datei hochzuladen, und es wurden die folgenden Metadaten hinzugefügt.
Es ist keine direkte Lösung mit dem ETag, aber es ist eine Möglichkeit, die gewünschten Metadaten (MD5) so aufzufüllen, dass Sie darauf zugreifen können. Es schlägt immer noch fehl, wenn jemand die Datei ohne Metadaten hochlädt.
quelle
Gemäß der AWS-Dokumentation ist der ETag weder ein MD5-Hash für einen mehrteiligen Upload noch für ein verschlüsseltes Objekt: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html
quelle
Hier ist der Algorithmus in Ruby ...
require 'digest' # PART_SIZE should match the chosen part size of the multipart upload # Set here as 10MB PART_SIZE = 1024*1024*10 class File def each_part(part_size = PART_SIZE) yield read(part_size) until eof? end end file = File.new('<path_to_file>') hashes = [] file.each_part do |part| hashes << Digest::MD5.hexdigest(part) end multipart_hash = Digest::MD5.hexdigest([hashes.join].pack('H*')) multipart_etag = "#{multipart_hash}-#{hashes.count}"
Dank kürzester Hex2Bin in Ruby und mehrteiligen Uploads auf S3 ...
quelle
Und hier ist eine PHP-Version der Berechnung des ETag:
function calculate_aws_etag($filename, $chunksize) { /* DESCRIPTION: - calculate Amazon AWS ETag used on the S3 service INPUT: - $filename : path to file to check - $chunksize : chunk size in Megabytes OUTPUT: - ETag (string) */ $chunkbytes = $chunksize*1024*1024; if (filesize($filename) < $chunkbytes) { return md5_file($filename); } else { $md5s = array(); $handle = fopen($filename, 'rb'); if ($handle === false) { return false; } while (!feof($handle)) { $buffer = fread($handle, $chunkbytes); $md5s[] = md5($buffer); unset($buffer); } fclose($handle); $concat = ''; foreach ($md5s as $indx => $md5) { $concat .= hex2bin($md5); } return md5($concat) .'-'. count($md5s); } } $etag = calculate_aws_etag('path/to/myfile.ext', 8);
Und hier ist eine erweiterte Version, die sich gegen einen erwarteten ETag verifizieren kann - und sogar die Chunksize erraten kann, wenn Sie es nicht wissen!
function calculate_etag($filename, $chunksize, $expected = false) { /* DESCRIPTION: - calculate Amazon AWS ETag used on the S3 service INPUT: - $filename : path to file to check - $chunksize : chunk size in Megabytes - $expected : verify calculated etag against this specified etag and return true or false instead - if you make chunksize negative (eg. -8 instead of 8) the function will guess the chunksize by checking all possible sizes given the number of parts mentioned in $expected OUTPUT: - ETag (string) - or boolean true|false if $expected is set */ if ($chunksize < 0) { $do_guess = true; $chunksize = 0 - $chunksize; } else { $do_guess = false; } $chunkbytes = $chunksize*1024*1024; $filesize = filesize($filename); if ($filesize < $chunkbytes && (!$expected || !preg_match("/^\\w{32}-\\w+$/", $expected))) { $return = md5_file($filename); if ($expected) { $expected = strtolower($expected); return ($expected === $return ? true : false); } else { return $return; } } else { $md5s = array(); $handle = fopen($filename, 'rb'); if ($handle === false) { return false; } while (!feof($handle)) { $buffer = fread($handle, $chunkbytes); $md5s[] = md5($buffer); unset($buffer); } fclose($handle); $concat = ''; foreach ($md5s as $indx => $md5) { $concat .= hex2bin($md5); } $return = md5($concat) .'-'. count($md5s); if ($expected) { $expected = strtolower($expected); $matches = ($expected === $return ? true : false); if ($matches || $do_guess == false || strlen($expected) == 32) { return $matches; } else { // Guess the chunk size preg_match("/-(\\d+)$/", $expected, $match); $parts = $match[1]; $min_chunk = ceil($filesize / $parts /1024/1024); $max_chunk = floor($filesize / ($parts-1) /1024/1024); $found_match = false; for ($i = $min_chunk; $i <= $max_chunk; $i++) { if (calculate_aws_etag($filename, $i) === $expected) { $found_match = true; break; } } return $found_match; } } else { return $return; } } }
quelle
Die kurze Antwort lautet, dass Sie den 128-Bit-Binär-MD5-Digest jedes Teils nehmen, sie zu einem Dokument verketten und dieses Dokument hashen. Der in dieser Antwort dargestellte Algorithmus ist genau.
Hinweis: Das mehrteilige ETAG-Formular mit dem Bindestrich ändert sich in das Formular ohne Bindestrich, wenn Sie den Blob "berühren" (auch ohne den Inhalt zu ändern). Das heißt, wenn Sie Ihr abgeschlossenes mehrteilig hochgeladenes Objekt (auch bekannt als PUT-COPY) kopieren oder direkt kopieren, berechnet S3 die ETAG mit der einfachen Version des Algorithmus neu. dh das Zielobjekt hat ein Etag ohne Bindestrich.
Sie haben dies wahrscheinlich bereits in Betracht gezogen, aber wenn Ihre Dateien weniger als 5 GB groß sind und Sie ihre MD5 bereits kennen und die Upload-Parallelisierung wenig bis gar keinen Vorteil bietet (z. B. wenn Sie den Upload von einem langsamen Netzwerk streamen oder von einer langsamen Festplatte hochladen) ), dann können Sie auch die Verwendung eines einfachen PUT anstelle eines mehrteiligen PUT in Betracht ziehen und Ihr bekanntes Content-MD5 in Ihren Anforderungsheadern übergeben - amazon schlägt den Upload fehl, wenn sie nicht übereinstimmen. Beachten Sie, dass Ihnen für jedes UploadPart eine Gebühr berechnet wird.
Darüber hinaus wird bei einigen Clients durch Übergeben eines bekannten MD5 für die Eingabe einer PUT-Operation der Client daran gehindert, den MD5 während der Übertragung neu zu berechnen. In boto3 (Python) würden Sie beispielsweise den
ContentMD5
Parameter der Methode client.put_object () verwenden. Wenn Sie den Parameter weglassen und den MD5 bereits kennen, verschwendet der Client Zyklen, in denen er vor der Übertragung erneut berechnet wird.quelle
Implementierung von 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}`; };
quelle
Ich habe eine Lösung für iOS und MacOS ohne externe Helfer wie dd und xxd. Ich habe es gerade gefunden, also melde ich es so wie es ist und plane, es zu einem späteren Zeitpunkt zu verbessern. Im Moment basiert es sowohl auf Objective-C- als auch auf Swift-Code. Erstellen Sie zunächst diese Hilfsklasse in Objective-C:
AWS3MD5Hash.h
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface AWS3MD5Hash : NSObject - (NSData *)dataFromFile:(FILE *)theFile startingOnByte:(UInt64)startByte length:(UInt64)length filePath:(NSString *)path singlePartSize:(NSUInteger)partSizeInMb; - (NSData *)dataFromBigData:(NSData *)theData startingOnByte:(UInt64)startByte length:(UInt64)length; - (NSData *)dataFromHexString:(NSString *)sourceString; @end NS_ASSUME_NONNULL_END
AWS3MD5Hash.m
#import "AWS3MD5Hash.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 256 @implementation AWS3MD5Hash - (NSData *)dataFromFile:(FILE *)theFile startingOnByte:(UInt64)startByte length:(UInt64)length filePath:(NSString *)path singlePartSize:(NSUInteger)partSizeInMb { char *buffer = malloc(length); NSURL *fileURL = [NSURL fileURLWithPath:path]; NSNumber *fileSizeValue = nil; NSError *fileSizeError = nil; [fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:&fileSizeError]; NSInteger __unused result = fseek(theFile,startByte,SEEK_SET); if (result != 0) { free(buffer); return nil; } NSInteger result2 = fread(buffer, length, 1, theFile); NSUInteger difference = fileSizeValue.integerValue - startByte; NSData *toReturn; if (result2 == 0) { toReturn = [NSData dataWithBytes:buffer length:difference]; } else { toReturn = [NSData dataWithBytes:buffer length:result2 * length]; } free(buffer); return toReturn; } - (NSData *)dataFromBigData:(NSData *)theData startingOnByte: (UInt64)startByte length:(UInt64)length { NSUInteger fileSizeValue = theData.length; NSData *subData; if (startByte + length > fileSizeValue) { subData = [theData subdataWithRange:NSMakeRange(startByte, fileSizeValue - startByte)]; } else { subData = [theData subdataWithRange:NSMakeRange(startByte, length)]; } return subData; } - (NSData *)dataFromHexString:(NSString *)string { string = [string lowercaseString]; NSMutableData *data= [NSMutableData new]; unsigned char whole_byte; char byte_chars[3] = {'\0','\0','\0'}; NSInteger i = 0; NSInteger length = string.length; while (i < length-1) { char c = [string characterAtIndex:i++]; if (c < '0' || (c > '9' && c < 'a') || c > 'f') continue; byte_chars[0] = c; byte_chars[1] = [string characterAtIndex:i++]; whole_byte = strtol(byte_chars, NULL, 16); [data appendBytes:&whole_byte length:1]; } return data; } @end
Erstellen Sie nun eine einfache schnelle Datei:
AWS Extensions.swift
import UIKit import CommonCrypto extension URL { func calculateAWSS3MD5Hash(_ numberOfParts: UInt64) -> String? { do { var fileSize: UInt64! var calculatedPartSize: UInt64! let attr:NSDictionary? = try FileManager.default.attributesOfItem(atPath: self.path) as NSDictionary if let _attr = attr { fileSize = _attr.fileSize(); if numberOfParts != 0 { let partSize = Double(fileSize / numberOfParts) var partSizeInMegabytes = Double(partSize / (1024.0 * 1024.0)) partSizeInMegabytes = ceil(partSizeInMegabytes) calculatedPartSize = UInt64(partSizeInMegabytes) if calculatedPartSize % 2 != 0 { calculatedPartSize += 1 } if numberOfParts == 2 || numberOfParts == 3 { // Very important when there are 2 or 3 parts, in the majority of times // the calculatedPartSize is already 8. In the remaining cases we force it. calculatedPartSize = 8 } if mainLogToggling { print("The calculated part size is \(calculatedPartSize!) Megabytes") } } } if numberOfParts == 0 { let string = self.memoryFriendlyMd5Hash() return string } let hasher = AWS3MD5Hash.init() let file = fopen(self.path, "r") defer { let result = fclose(file)} var index: UInt64 = 0 var bigString: String! = "" var data: Data! while autoreleasepool(invoking: { if index == (numberOfParts-1) { if mainLogToggling { //print("Siamo all'ultima linea.") } } data = hasher.data(from: file!, startingOnByte: index * calculatedPartSize * 1024 * 1024, length: calculatedPartSize * 1024 * 1024, filePath: self.path, singlePartSize: UInt(calculatedPartSize)) bigString = bigString + MD5.get(data: data) + "\n" index += 1 if index == numberOfParts { return false } return true }) {} let final = MD5.get(data :hasher.data(fromHexString: bigString)) + "-\(numberOfParts)" return final } catch { } return nil } func memoryFriendlyMd5Hash() -> String? { let bufferSize = 1024 * 1024 do { // Open file for reading: let file = try FileHandle(forReadingFrom: self) defer { file.closeFile() } // Create and initialize MD5 context: var context = CC_MD5_CTX() CC_MD5_Init(&context) // Read up to `bufferSize` bytes, until EOF is reached, and update MD5 context: while autoreleasepool(invoking: { let data = file.readData(ofLength: bufferSize) if data.count > 0 { data.withUnsafeBytes { _ = CC_MD5_Update(&context, $0, numericCast(data.count)) } return true // Continue } else { return false // End of file } }) { } // Compute the MD5 digest: var digest = Data(count: Int(CC_MD5_DIGEST_LENGTH)) digest.withUnsafeMutableBytes { _ = CC_MD5_Final($0, &context) } let hexDigest = digest.map { String(format: "%02hhx", $0) }.joined() return hexDigest } catch { print("Cannot open file:", error.localizedDescription) return nil } } struct MD5 { static func get(data: Data) -> String { var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) let _ = data.withUnsafeBytes { bytes in CC_MD5(bytes, CC_LONG(data.count), &digest) } var digestHex = "" for index in 0..<Int(CC_MD5_DIGEST_LENGTH) { digestHex += String(format: "%02x", digest[index]) } return digestHex } // The following is a memory friendly version static func get2(data: Data) -> String { var currentIndex = 0 let bufferSize = 1024 * 1024 //var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) // Create and initialize MD5 context: var context = CC_MD5_CTX() CC_MD5_Init(&context) while autoreleasepool(invoking: { var subData: Data! if (currentIndex + bufferSize) < data.count { subData = data.subdata(in: Range.init(NSMakeRange(currentIndex, bufferSize))!) currentIndex = currentIndex + bufferSize } else { subData = data.subdata(in: Range.init(NSMakeRange(currentIndex, data.count - currentIndex))!) currentIndex = currentIndex + (data.count - currentIndex) } if subData.count > 0 { subData.withUnsafeBytes { _ = CC_MD5_Update(&context, $0, numericCast(subData.count)) } return true } else { return false } }) { } // Compute the MD5 digest: var digest = Data(count: Int(CC_MD5_DIGEST_LENGTH)) digest.withUnsafeMutableBytes { _ = CC_MD5_Final($0, &context) } var digestHex = "" for index in 0..<Int(CC_MD5_DIGEST_LENGTH) { digestHex += String(format: "%02x", digest[index]) } return digestHex } }
Fügen Sie nun hinzu:
#import "AWS3MD5Hash.h"
zu Ihrem Objective-C Bridging-Header. Sie sollten mit diesem Setup einverstanden sein.
Anwendungsbeispiel
Um dieses Setup zu testen, können Sie die folgende Methode innerhalb des Objekts aufrufen, das für die Verarbeitung der AWS-Verbindungen zuständig ist:
func getMd5HashForFile() { let credentialProvider = AWSCognitoCredentialsProvider(regionType: AWSRegionType.USEast2, identityPoolId: "<INSERT_POOL_ID>") let configuration = AWSServiceConfiguration(region: AWSRegionType.APSoutheast2, credentialsProvider: credentialProvider) configuration?.timeoutIntervalForRequest = 3.0 configuration?.timeoutIntervalForResource = 3.0 AWSServiceManager.default().defaultServiceConfiguration = configuration AWSS3.register(with: configuration!, forKey: "defaultKey") let s3 = AWSS3.s3(forKey: "defaultKey") let headObjectRequest = AWSS3HeadObjectRequest()! headObjectRequest.bucket = "<NAME_OF_YOUR_BUCKET>" headObjectRequest.key = self.latestMapOnServer.key let _: AWSTask? = s3.headObject(headObjectRequest).continueOnSuccessWith { (awstask) -> Any? in let headObjectOutput: AWSS3HeadObjectOutput? = awstask.result var ETag = headObjectOutput?.eTag! // Here you should parse the returned Etag and extract the number of parts to provide to the helper function. Etags end with a "-" followed by the number of parts. If you don't see this format, then pass 0 as the number of parts. ETag = ETag!.replacingOccurrences(of: "\"", with: "") print("headObjectOutput.ETag \(ETag!)") let mapOnDiskUrl = self.getMapsDirectory().appendingPathComponent(self.latestMapOnDisk!) let hash = mapOnDiskUrl.calculateAWSS3MD5Hash(<Take the number of parts from the ETag returned by the server>) if hash == ETag { print("They are the same.") } print ("\(hash!)") return nil } }
Wenn der vom Server zurückgegebene ETag am Ende des ETag kein "-" hat, übergeben Sie einfach 0 an berechneAWSS3MD5Hash. Bitte kommentieren Sie, wenn Sie auf Probleme stoßen. Ich arbeite an einer schnellen Lösung. Ich werde diese Antwort aktualisieren, sobald ich fertig bin. Vielen Dank
quelle
Eine Version in Rust:
use crypto::digest::Digest; use crypto::md5::Md5; use std::fs::File; use std::io::prelude::*; use std::iter::repeat; fn calculate_etag_from_read(f: &mut dyn Read, chunk_size: usize) -> Result<String> { let mut md5 = Md5::new(); let mut concat_md5 = Md5::new(); let mut input_buffer = vec![0u8; chunk_size]; let mut chunk_count = 0; let mut current_md5: Vec<u8> = repeat(0).take((md5.output_bits() + 7) / 8).collect(); let md5_result = loop { let amount_read = f.read(&mut input_buffer)?; if amount_read > 0 { md5.reset(); md5.input(&input_buffer[0..amount_read]); chunk_count += 1; md5.result(&mut current_md5); concat_md5.input(¤t_md5); } else { if chunk_count > 1 { break format!("{}-{}", concat_md5.result_str(), chunk_count); } else { break md5.result_str(); } } }; Ok(md5_result) } fn calculate_etag(file: &String, chunk_size: usize) -> Result<String> { let mut f = File::open(file)?; calculate_etag_from_read(&mut f, chunk_size) }
Sehen Sie sich ein Repo mit einer einfachen Implementierung an: https://github.com/bn3t/calculate-etag/tree/master
quelle
Hier ist noch ein Teil dieses verrückten AWS-Herausforderungspuzzles.
FWIW, diese Antwort setzt voraus, dass Sie bereits herausgefunden haben, wie der "MD5 von MD5-Teilen" berechnet wird, und dass Sie Ihr mehrteiliges AWS-ETag aus allen anderen hier bereits bereitgestellten Antworten neu erstellen können.
Was diese Antwort anspricht, ist der Ärger, die ursprüngliche Upload-Teilegröße "erraten" oder auf andere Weise "erraten" zu müssen.
Wir verwenden verschiedene Tools zum Hochladen in S3 und alle scheinen unterschiedliche Upload-Teilegrößen zu haben, daher war "Raten" wirklich keine Option. Außerdem haben wir viele Dateien, die historisch hochgeladen wurden, als die Teilegrößen unterschiedlich zu sein schienen. Der alte Trick, eine interne Serverkopie zu verwenden, um die Erstellung eines ETag vom Typ MD5 zu erzwingen, funktioniert ebenfalls nicht mehr, da AWS seine internen Serverkopien so geändert hat, dass sie auch mehrteilig sind (nur mit einer relativ großen Teilegröße).
Also ... Wie können Sie die Teilegröße des Objekts herausfinden?
Wenn Sie zuerst eine head_object-Anfrage stellen und feststellen, dass es sich bei dem ETag um ein mehrteiliges ETag handelt (das am Ende ein '- <partcount>' enthält), können Sie eine weitere head_object-Anfrage stellen, jedoch mit einem zusätzlichen part_number-Attribut von 1 (der erste Teil). Diese nachfolgende head_object-Anfrage gibt Ihnen dann die content_length des ersten Teils zurück. Viola ... Jetzt kennen Sie die verwendete Teilegröße und können diese Größe verwenden, um Ihr lokales ETag neu zu erstellen, das mit dem ursprünglich hochgeladenen S3-ETag übereinstimmen sollte, das beim Hochladen des Objekts erstellt wurde.
Wenn Sie genau sein möchten (möglicherweise sollten einige mehrteilige Uploads variable Teilegrößen verwenden), können Sie weiterhin head_object-Anforderungen mit jeder angegebenen Teilenummer aufrufen und die MD5 jedes Teils aus der zurückgegebenen Inhaltsinhaltslänge berechnen.
Hoffentlich hilft das...
quelle
In Bezug auf die Blockgröße habe ich festgestellt, dass dies anscheinend von der Anzahl der Teile abhängt. Die maximale Anzahl von Teilen beträgt 10000 als AWS-Dokumente.
Ab einem Standardwert von 8 MB und Kenntnis der Dateigröße, der Blockgröße und der Teile können Sie also wie folgt berechnen:
Teile müssen abgerundet sein
quelle
Nein,
Bis jetzt gibt es keine Lösung, um die normale Datei ETag und die mehrteilige Datei ETag und MD5 der lokalen Datei abzugleichen.
quelle