0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PowerShellでS3のEtagを計算してみる(暗号化なし&SSE-S3&マルチパートアップロード対応)

Last updated at Posted at 2023-09-28

上記のようにS3にアップロードされた際の、Eタグについて3つほど記事を投稿しました。
S3ファイルのEタグについてまとめると下記のようになっていました。

方式 マルチパートアップロード Eタグの値
暗号化なし なし MD5 ダイジェスト
暗号化なし あり 分割したファイルごとにMD5を計算。計算された値を連結。連結した値のMD5ダイジェスト
sse-s3 なし MD5 ダイジェスト
sse-s3 あり 分割したファイルごとにMD5を計算。計算された値を連結。連結した値のMD5ダイジェスト
sse-kms なし MD5 ダイジェストではない(詳細は公開されていない(多分))
sse-kms あり MD5 ダイジェストではない(詳細は公開されていない(多分))
sse-c なし MD5 ダイジェストではない(詳細は公開されていない(多分))
sse-c あり MD5 ダイジェストではない(詳細は公開されていない(多分))

マルチパートアップロードではない場合は、MD5を計算すればよいのですが。
マルチパートアップロードの場合は、個別に計算する必要があり、bashで実装された計算ツールを紹介しました(antespi/s3md5)。

今回、Eタグの値についてPowerShellで計算するスクリプトを書いてみたので紹介します。

#!/usr/bin/env pwsh

<#
.SYNOPSIS
    S3のEtag計算ツール
.DESCRIPTION
    S3のEtagは暗号化なし、もしくはSSE-S3の場合のみEtagの計算形式については公開されている
    このため暗号化なし、SSE-S3の場合はEtagの値について計算できますが。
    SSE-KMS , SSE-CについてはEtagについて情報が多分公開されていないため値は計算できません。
.EXAMPLE
    PS > .\Get-FileEtag.ps1 -Path .\testfile -ChunkByteSize 8mb
    任意のファイルを分割サイズ 8mb で計算
.EXAMPLE
    PS > .\Get-FileEtag.ps1 -Path .\foobar -UploadFromAWSManagementConsole
    マネジメントコンソールからアップロードしたファイルの場合
#>

[cmdletbinding()]
param (
    [Parameter(mandatory = $true)]
    [ValidateScript({
            if ( -Not (Test-Path -Path $_ -PathType Leaf) ) {
                throw "file is not exist:$_"
            }
            return $true
        })]
    [System.IO.FileInfo]$Path,
    [Parameter(mandatory = $false)]
    [ValidateScript({
            # 5MiBより小さい、5GiBより大きい場合はエラー
            if ( $_ -lt 5MB -or $_ -gt 5GB ) {
                throw "ChunkByteSize is 5 MB to 5 GB"
            }
            return $true
        })]
    [int64]$ChunkByteSize = 8MB,
    [Parameter(mandatory = $false)]
    [Switch]$UploadFromAWSManagementConsole
)

PROCESS {

    Set-StrictMode -Version Latest
    $ErrorActionPreference = "stop"

    $absolutepath = Resolve-Path $Path

    if ($UploadFromAWSManagementConsole) {
        # マネジメントコンソールからアップロードされた場合はchunksize 17179870 byte
        $target_chunk_byte = 17179870
    }
    else {
        $target_chunk_byte = $ChunkByteSize
    }

    Write-Host $("calculation targets is {0}" -f $absolutepath)
    Write-Host $("ChunkByteSize is {0} byte" -f $target_chunk_byte)

    [byte[]]$hash_array = New-Object system.Array[] 0

    # 対象ファイルを読み込み
    [byte[]]$bytestream = [System.IO.File]::ReadAllBytes($absolutepath)

    # 分割数を算出
    $split_count = [math]::ceiling($bytestream.Length / $target_chunk_byte)

    $cursor = 0

    # 各分割ファイルごとにMD5を計算
    # splib_byte ごとに で split_count -1 回 MD5を計算
    for ($i = 1; $i -lt $split_count; $i++) {
        $new_entry = New-Object -TypeName byte[] -ArgumentList $target_chunk_byte
        # 対象ファイルから分割部分を抽出
        [System.Array]::copy($bytestream, $cursor, $new_entry, 0 , $target_chunk_byte)
        # 抽出した部分のMD5を計算
        $hash_part = [System.Security.Cryptography.HashAlgorithm]::Create("MD5").ComputeHash($new_entry)
        $hash_array += $hash_part
        # カーソルをシフト
        $cursor = $i * $target_chunk_byte
    }

    # 最終ファイルのみsplib_byte以下のファイルサイズ
    $lastfilesize = $bytestream.Length - ($split_count - 1) * $target_chunk_byte
    $new_entry = New-Object -TypeName byte[] -ArgumentList $lastfilesize
    # 対象ファイルから最終分割部分を抽出
    [System.Array]::copy($bytestream, $cursor, $new_entry, 0 , $lastfilesize)
    # 抽出した部分のMD5を計算
    $hash_part = [System.Security.Cryptography.HashAlgorithm]::Create("MD5").ComputeHash($new_entry)
    $hash_array += $hash_part

    if ($split_count -gt 1 ) {
        # ファイルごとに計算して加算した値からmd5を取得
        $hash_string = $([System.Security.Cryptography.HashAlgorithm]::Create("MD5").ComputeHash($hash_array) | ForEach-Object { $_.ToString("x2") }) -join ""
        # 末尾に分割数を追加
        $etag = "$hash_string-{0}" -f $split_count
    }
    else {
        # 分割なし、計算したMD5
        $hash_string = ($hash_array | ForEach-Object { $_.ToString("x2") }) -join ""
        $etag = $hash_string
    }

    Write-Host $("Etag is {0}" -f $etag)
}

マルチパートアップロード時の分割サイズについて補足

aws-cliからファイルをマルチパートアップロードするさいの個々のファイルサイズについて、multipart_chunksizeで設定されており、このデフォルト値は8MB(8388608 Byte)となります。

AWS CLI S3 Configuration

またブラウザからマネジメントコンソールを使ってアップロードする際は、17179870 Byteとなります。
詳細は下記記事参照。

AWSマネジメントコンソールからS3にマルチパートアップロードした時の分割サイズについて

使い方

aws-cliから8mb分割でsse-s3でアップロードしたファイルの場合

.\Get-FileEtag.ps1 -Path .\100mb

image.png

image.png

aws-cliから16mb分割でsse-s3でアップロードしたファイルの場合

.\Get-FileEtag.ps1 -Path .\100mb -ChunkByteSize 16mb

image.png

image.png

マネジメントコンソールからアップロードした場合

image.png

image.png

総評

S3のEtagでのファイル整合性チェック、調べ始めてみると。

暗号化の有無、マルチパートアップロードの有無、chunksize(aws-cliのデフォルト値8mb、マネジメントコンソールの値(17179870 Byte)と色々あり。

要素によって値が異なり、また暗号化の形式によってはEtagをファイルの整合性確認に利用できない事がわかりました。

いろいろと気をつけるポイントありますね。

個人的にはファイルの整合性確認するのには、Etagではなく2022年に追加されたチェックサムアルゴリズムを素直に利用するのがよさそうに思いました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?