大きなビデオファイル用のMD5チェックサムの作成に取り組んでいます。私は現在コードを使用しています:
extension NSData {
func MD5() -> NSString {
let digestLength = Int(CC_MD5_DIGEST_LENGTH)
let md5Buffer = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLength)
CC_MD5(bytes, CC_LONG(length), md5Buffer)
let output = NSMutableString(capacity: Int(CC_MD5_DIGEST_LENGTH * 2))
for i in 0..<digestLength {
output.appendFormat("%02x", md5Buffer[i])
}
return NSString(format: output)
}
}
しかし、それはメモリバッファを作成し、大きなビデオファイルには理想的ではありません。 Swiftでファイルストリームを読み取るMD5チェックサムを計算する方法があるので、メモリフットプリントは最小限になりますか?
たとえば、次のように、MD5チェックサムをチャンクで計算できます。 in 入力全体を同時に必要としないMD5ライブラリはありますか? 。
Swift(現在Swift 5)用に更新されています)を使用した可能な実装は次のとおりです。
_import CommonCrypto
func md5File(url: URL) -> Data? {
let bufferSize = 1024 * 1024
do {
// Open file for reading:
let file = try FileHandle(forReadingFrom: url)
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.baseAddress, numericCast(data.count))
}
return true // Continue
} else {
return false // End of file
}
}) { }
// Compute the MD5 digest:
var digest: [UInt8] = Array(repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
_ = CC_MD5_Final(&digest, &context)
return Data(digest)
} catch {
print("Cannot open file:", error.localizedDescription)
return nil
}
}
_
自動解放プールは、file.readData()
によって返されたメモリを解放するために必要です。自動解放プールがないと、ファイル全体(潜在的に巨大な)がメモリにロードされます。そのことに気づき、実装を提供してくれたAbhiBeckertに感謝します。
16進エンコードされた文字列としてダイジェストが必要な場合は、戻り値の型を_String?
_に変更し、
_return digest
_
沿って
_let hexDigest = digest.map { String(format: "%02hhx", $0) }.joined()
return hexDigest
_
SHA256ハッシュの解決策(Martin Rの回答に基づく):
func sha256(url: URL) -> Data? {
do {
let bufferSize = 1024 * 1024
// Open file for reading:
let file = try FileHandle(forReadingFrom: url)
defer {
file.closeFile()
}
// Create and initialize SHA256 context:
var context = CC_SHA256_CTX()
CC_SHA256_Init(&context)
// Read up to `bufferSize` bytes, until EOF is reached, and update SHA256 context:
while autoreleasepool(invoking: {
// Read up to `bufferSize` bytes
let data = file.readData(ofLength: bufferSize)
if data.count > 0 {
data.withUnsafeBytes {
_ = CC_SHA256_Update(&context, $0, numericCast(data.count))
}
// Continue
return true
} else {
// End of file
return false
}
}) { }
// Compute the SHA256 digest:
var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
digest.withUnsafeMutableBytes {
_ = CC_SHA256_Final($0, &context)
}
return digest
} catch {
print(error)
return nil
}
}
以前に作成された名前URL
のタイプfileURL
のインスタンスでの使用法:
if let digestData = sha256(url: fileURL) {
let calculatedHash = digestData.map { String(format: "%02hhx", $0) }.joined()
DDLogDebug(calculatedHash)
}