xlogI125’s blog

パソコン作業を効率化したい

Webブラウザ JavaScript ハッシュ値

crypto.subtle.digestでファイルのハッシュ値を求める。
PowerShellGet-FileHashコマンドレットではなく、Webブラウザでファイルのハッシュ値を求める方法を考える。

filesプロパティ(<input type="file">)

選択されたファイルのハッシュ値を出力

// <!doctype html>
// <html><body><script>
"use strict";

// テスト環境: Edge, Firefox, Windows 11 (2024年4月頃)

// スタイルシート
let sheet = new CSSStyleSheet();
sheet.insertRule("table { border-collapse: collapse; }");
sheet.insertRule("th, td { border: 1px solid black; padding: 0.5em; }");
document.adoptedStyleSheets = [sheet];

// input 要素
let elmInputFile = document.createElement("input");
elmInputFile.type = "file";
elmInputFile.multiple = true;
document.body.appendChild(elmInputFile);

document.body.appendChild(document.createElement("br"));

// button 要素
let elmButton = document.createElement("button");
elmButton.innerText = "クリック";
document.body.appendChild(elmButton);

document.body.appendChild(document.createElement("br"));

// ボタン click
elmButton.onclick = function() {
  // table 要素
  let elmTable = document.createElement("table");
  document.body.appendChild(elmTable);
  let elmTbody = document.createElement("tbody");
  elmTable.appendChild(elmTbody);
  elmTbody.insertAdjacentHTML("beforeend", "<tr><th>Name</th><th>Hash</th></tr>") 

  // crypto.subtle.digest
  for (const file of elmInputFile.files) {
    file.arrayBuffer().then(arrBuf => {
      crypto.subtle.digest("SHA-256", arrBuf).then(arrBuf => {
        const strHash = Array.from(new Uint8Array(arrBuf)).map(byte => byte.toString(16).padStart(2, "0")).join("").toUpperCase();
        elmTbody.insertAdjacentHTML("beforeend", `<tr><td>${file.name}</td><td>${strHash}</th></td>`) 
      });
    });
  }

  document.body.appendChild(document.createElement("br"));
};
// </script></body></html>

dataTransfer.files

Fileオブジェクトのnameプロパティなどを表示

// <!doctype html>
// <html><body><script>
"use strict";

// テスト環境: Edge, Firefox, Windows 11 (2024年4月頃)

let elmTextarea = document.createElement("textarea");
document.body.appendChild(elmTextarea);

elmTextarea.textContent = "ここにファイルをドラッグ&ドロップ";

// dragover イベント
elmTextarea.addEventListener("dragover", function(event) {
  event.preventDefault();
  console.log(event);
  event.target.textContent = "dragover イベント";
});

// drop イベント
elmTextarea.addEventListener("drop", function(event) {
  event.preventDefault();
  console.log(event);
  event.target.textContent = "";

  // dataTransfer プロパティ
  for (const file of event.dataTransfer.files) {
    event.target.textContent += JSON.stringify({ name: file.name, type: file.type, size: file.size }) + "\n";
  }

});
// </script></body></html>

参考: Get-FileHashコマンドレット

フォームにドラッグされたファイルのハッシュ値をテキストボックスに表示

# PowerShell 5.1, Windows 11 (2024年4月頃)

$ErrorActionPreference = "Stop"
$VerbosePreference = "Continue"

Set-StrictMode -Version Latest

Add-Type -Language CSharp -Namespace Win32API -Name Shlwapi -MemberDefinition @'
  [DllImport("Shlwapi.dll", EntryPoint = "StrCmpLogicalW", CharSet = CharSet.Unicode)]
  public static extern int StrCmpLogicalW([MarshalAs(UnmanagedType.LPWStr)] string psz1, [MarshalAs(UnmanagedType.LPWStr)] string psz2);
'@

Add-Type -AssemblyName System.Windows.Forms

# フォーム
$form = [Windows.Forms.Form]::new()
$form.Text = "ファイルをドラッグ&ドロップ"
$form.Size = [System.Drawing.Size]::new(800, 600)
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$form.AllowDrop = $true

# テキストボックス
$textBox = [System.Windows.Forms.TextBox]::new()
$textBox.Size = [System.Drawing.Size]::new(
  800 - [System.Windows.Forms.SystemInformation]::VerticalScrollBarWidth, 
  600 - [System.Windows.Forms.SystemInformation]::HorizontalScrollBarHeight - [System.Windows.Forms.SystemInformation]::CaptionHeight
)
$textBox.AutoSize = $false
$textBox.Font = [System.Drawing.Font]::new("MS ゴシック", 12)
$textBox.Multiline = $true
$textBox.AcceptsReturn = $true
$textBox.WordWrap = $false
$textBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Both
$textBox.ShortcutsEnabled = $true

$form.Controls.Add($textBox)

# DragEnterイベント
$form.add_DragEnter({
  param([object]$sender, [System.Windows.Forms.DragEventArgs]$e)
  try {
    $e.Effect = [System.Windows.Forms.DragDropEffects]::Copy
  }
  catch {
    $_ | Write-Verbose
  }
} -as [System.Windows.Forms.DragEventHandler])

# DragDropイベント
$form.add_DragDrop({
  param([object]$sender, [System.Windows.Forms.DragEventArgs]$e)
  try {
    # ファイルのパス
    $paths = [string[]]$e.Data.GetFileDropList()
    # パスをソート
    [System.Array]::Sort($paths, { param([string]$x, [string]$y); return [Win32API.Shlwapi]::StrCmpLogicalW($x, $y) } -as [System.Comparison[string]])
    # ファイルのハッシュ値
    $fileHashs = Get-FileHash -LiteralPath $paths

    foreach ($fileHash in $fileHashs) {
      # 空白行を削除
      $strs = ($fileHash | Format-List -Property @{ Name = "Name"; Expression = { [System.IO.Path]::GetFileName($_.Path) } }, Hash, Algorithm | Out-String -Width 80 -Stream) -ne ""
      $str = $strs -join "`r`n"
      # ファイルのハッシュ値をテキストボックスに表示
      $textBox.Text += $str + "`r`n`r`n"
    }

    # テキストボックスをスクロール
    $textBox.Select($textBox.Text.Length, 0)
    $textBox.ScrollToCaret()
  }
  catch {
    $_ | Write-Verbose
  }
} -as [System.Windows.Forms.DragEventHandler])

$form.ShowDialog()