Полезное⚜️
December 22, 2024

Замена даты создания файла на exif

Несколько лет назад я потерял все свои фотографии, которые хранил на внешнем жестком диске. Для упорядочивания файлов было написано несколько скриптов, и этот один из них.

Скрипт ищет дату создания фотографии (её проставляет фотоаппарат) и подставляет в поле "создано".

Важно: Для работы со свойствами EXIF в PowerShell используется класс [System.Drawing.Bitmap], являющийся частью .NET Framework. Однако в некоторых современных версиях PowerShell этот класс может быть не доступен по умолчанию.
Если при запуске скрипта возникнут ошибки, убедитесь, что у вас установлена .NET Framework (Windows PowerShell 5.1 обычно идет вместе с .NET Framework) или используйте другой способ чтения EXIF (например, утилиту exiftool).
#requires -version 3

<#
.SYNOPSIS
  Скрипт рекурсивно ищет файлы изображений, получает EXIF-дату (дата/время съемки) 
  и ставит её в качестве даты создания файла. Если EXIF не найдена – выводит имя файла в консоль.

.DESCRIPTION
  1. Рекурсивно обходит все подпапки в указанных путях (по умолчанию - текущая директория).
  2. Ищет файлы изображений по заданным расширениям: jpg, jpeg, png, gif, tiff, bmp.
  3. С помощью .NET-класса [System.Drawing.Bitmap] считывает EXIF-свойства.
  4. Берёт значение EXIF-тега DateTimeOriginal (Id 0x9003), парсит его в дату и 
     назначает её как CreationTime и LastWriteTime файла.
  5. Если EXIF-дата отсутствует, выводит сообщение в консоль и ничего с файлом не делает.

.NOTES
  Автор: ChatGPT (пример)
  Требования:
    - PowerShell 3.0+
    - .NET Framework (для класса System.Drawing.Bitmap)
#>

param(
    [Parameter(Mandatory=$false, Position=0)]
    [string]
    $Path = "."
)

# Подключаем сборку System.Drawing, если она ещё не загружена.
# В некоторых окружениях (например, Windows PowerShell 5.1) достаточно, в других может быть недоступна.
try {
    Add-Type -AssemblyName System.Drawing
}
catch {
    Write-Host "Не удалось загрузить сборку System.Drawing. Убедитесь, что установлена .NET Framework или попробуйте другой метод чтения EXIF." -ForegroundColor Red
    return
}

# Список расширений, которые считаем изображениями
$imageExtensions = '.jpg', '.jpeg', '.png'

# Получаем список всех файлов с нужными расширениями рекурсивно из каталога $Path
Write-Host "Поиск изображений в папке: $Path`..."
$files = Get-ChildItem -Path $Path -Recurse -File | Where-Object {
    $imageExtensions -contains $_.Extension.ToLower()
}

if ($files.Count -eq 0) {
    Write-Host "Не найдено файлов с расширениями: $($imageExtensions -join ', ')"
    return
}

foreach ($file in $files) {
    try {
        $bitmap = New-Object System.Drawing.Bitmap($file.FullName)

        # Ищем EXIF-тег DateTimeOriginal (0x9003).
        # Для некоторых снимков старых форматов может использоваться 0x0132 (DateTime) или 0x9004 (DateTimeDigitized).
        # Но самый распространённый - 0x9003 (DateTimeOriginal).
        $exifItem = $bitmap.PropertyItems | Where-Object { $_.Id -eq 0x9003 }

        if ($exifItem) {
            # EXIF-дата хранится как ASCII-строка формата "yyyy:MM:dd HH:mm:ss"
            $exifDateString = [System.Text.Encoding]::ASCII.GetString($exifItem.Value).Trim([char]0)
            if ([string]::IsNullOrWhiteSpace($exifDateString)) {
                Write-Host "EXIF-дата отсутствует (пустая) в файле: $($file.FullName)"
                $bitmap.Dispose()
                continue
            }

            # Парсим дату. Формат обычно: "YYYY:MM:dd HH:mm:ss"
            $format = "yyyy:MM:dd HH:mm:ss"
            $exifDate = [datetime]::ParseExact($exifDateString, $format, $null)

            # Устанавливаем дату создания (CreationTime) и дату изменения (LastWriteTime) файла
            [System.IO.File]::SetCreationTime($file.FullName, $exifDate)
            [System.IO.File]::SetLastWriteTime($file.FullName, $exifDate)

            # (опционально) Можете оставить только CreationTime или только LastWriteTime
            # [System.IO.File]::SetCreationTime($file.FullName, $exifDate)

            Write-Host "Обновлена дата для: $($file.FullName) => $exifDate"
        }
        else {
            Write-Host "EXIF-дата не найдена в файле: $($file.FullName)"
        }

        $bitmap.Dispose()
    }
    catch {
        Write-Warning "Ошибка при обработке файла $($file.FullName). Текст ошибки: $_"
    }
}

Для запуска надо сохранить скрипт в файл, например в Set-ExifDate.ps1. Пример команды для запуска:

.\Set-ExifDate.ps1 -Path "C:\MyPhotos"