diff --git a/powershell/ImgBB_Uploader/ImgBB_Uploader.ps1 b/powershell/ImgBB_Uploader/ImgBB_Uploader.ps1 index 01c99e7..cf535ef 100644 --- a/powershell/ImgBB_Uploader/ImgBB_Uploader.ps1 +++ b/powershell/ImgBB_Uploader/ImgBB_Uploader.ps1 @@ -13,12 +13,18 @@ retries/skips, and red for errors. Text labels remain readable without color. .PARAMETER DirectoryPath An existing directory. Treated as a literal path, including brackets. + Required unless FilePath is supplied. Ignored when FilePath is supplied. +.PARAMETER FilePath + One existing JPG, JPEG, or PNG file, interpreted literally. Takes precedence + over DirectoryPath. An invalid FilePath fails without falling back to a + directory scan. Recurse has no effect in single-file mode. .PARAMETER ApiKey ImgBB API key. A nonblank value overrides IMGBB_API_KEY. Prefer the environment variable to avoid saving a key in shell history. The key is sent in the POST body, never in the URL or script-generated messages. .PARAMETER Recurse Include subdirectories. Disabled by default. Hidden files are not included. + Ignored when FilePath is supplied. .PARAMETER WaitTime Random (default) enables a random 3-6 second pause between requests. Only Off disables this ordinary pause. Omitted, empty, or unrecognized @@ -43,6 +49,11 @@ .EXAMPLE .\Upload-ImagesToImgBB.ps1 -DirectoryPath './images' -WaitTime Off Disables ordinary request delays while retaining retry waits. +.EXAMPLE + .\Upload-ImagesToImgBB.ps1 -FilePath 'C:\Images\photo.jpg' -DryRun +.EXAMPLE + .\Upload-ImagesToImgBB.ps1 -FilePath 'C:\Images\photo.jpg' -DirectoryPath 'C:\OtherImages' + Uploads only photo.jpg; DirectoryPath is ignored. .NOTES Intended for Windows PowerShell 5.1 and PowerShell 7. Exit codes: 0 = success (including no matching files); 1 = any failure. @@ -78,10 +89,8 @@ [CmdletBinding()] param ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] [string]$DirectoryPath, - + [string]$FilePath, [string]$ApiKey, [AllowNull()] [AllowEmptyString()] @@ -170,6 +179,37 @@ if ([string]::IsNullOrWhiteSpace($finalApiKey)) { exit 1 } +# Presence, rather than validity, determines precedence. Never silently upload +# a directory when the caller explicitly selected a missing or invalid file. +$useFilePath = $PSBoundParameters.ContainsKey('FilePath') +$supportedExtensions = @('.jpg', '.jpeg', '.png') +$inputError = if ($useFilePath) { + 'FilePath must identify an existing, accessible JPG, JPEG, or PNG file. DirectoryPath is ignored.' +} +else { 'DirectoryPath must identify an existing, accessible filesystem directory, or supply -FilePath.' } + +try { + if ($useFilePath) { + if ([string]::IsNullOrWhiteSpace($FilePath)) { throw 'Empty file path.' } + $selectedFile = Get-Item -LiteralPath $FilePath -ErrorAction Stop + if ($selectedFile.PSIsContainer -or $selectedFile.PSProvider.Name -ne 'FileSystem' -or + $selectedFile.Extension -notin $supportedExtensions) { + throw 'Unsupported file input.' + } + } + else { + if ([string]::IsNullOrWhiteSpace($DirectoryPath)) { throw 'Missing directory path.' } + $directory = Get-Item -LiteralPath $DirectoryPath -ErrorAction Stop + if (-not $directory.PSIsContainer -or $directory.PSProvider.Name -ne 'FileSystem') { + throw 'A filesystem directory is required.' + } + } +} +catch { + Write-UploadError $inputError + exit 1 +} + $processed = 0L $succeeded = 0L $failed = 0L @@ -183,25 +223,32 @@ $endpoint = 'https://api.imgbb.com/1/upload' $originalSecurityProtocol = [Net.ServicePointManager]::SecurityProtocol try { - $directory = Get-Item -LiteralPath $DirectoryPath -ErrorAction Stop - if (-not $directory.PSIsContainer -or $directory.PSProvider.Name -ne 'FileSystem') { - throw 'A filesystem directory is required.' - } - if (-not $DryRun -and $PSVersionTable.PSVersion.Major -eq 5) { [Net.ServicePointManager]::SecurityProtocol = $originalSecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } $mode = if ($DryRun) { 'Dry run' } else { 'Upload' } - Write-Status "$mode started. Recursive search: $([bool]$Recurse)." + if ($useFilePath) { + Write-Status "$mode started. Single file: $($selectedFile.FullName)." + } + else { + Write-Status "$mode started. Recursive search: $([bool]$Recurse)." + } if (-not $DryRun) { $waitMode = if ($useRandomWait) { 'Random (3-6 seconds)' } else { 'Off (retry waits still apply)' } Write-Status "Request delay: $waitMode." } - Get-ChildItem -LiteralPath $directory.FullName -File -Recurse:$Recurse -ErrorAction Stop | - Where-Object { $_.Extension -in @('.jpg', '.jpeg', '.png') } | - ForEach-Object { + # Both modes feed the same preparation/upload pipeline; directory mode + # continues to stream files instead of collecting the full list in memory. + $getSelectedImages = { + if ($useFilePath) { $selectedFile } + else { + Get-ChildItem -LiteralPath $directory.FullName -File -Recurse:$Recurse -ErrorAction Stop | + Where-Object { $_.Extension -in $supportedExtensions } + } + } + & $getSelectedImages | ForEach-Object { $image = $_ $processed++ $base64Image = $null @@ -301,7 +348,7 @@ try { } catch { $runFailed = $true - Write-UploadError 'Run stopped. Check the directory and its permissions; if requests failed, check the API key, connection, or service availability before restarting.' + Write-UploadError 'Run stopped. Check the selected input and its permissions; if requests failed, check the API key, connection, or service availability before restarting.' } finally { if ($PSVersionTable.PSVersion.Major -eq 5) { diff --git a/powershell/ImgBB_Uploader/README.md b/powershell/ImgBB_Uploader/README.md index b622045..2b0305b 100644 --- a/powershell/ImgBB_Uploader/README.md +++ b/powershell/ImgBB_Uploader/README.md @@ -1,13 +1,13 @@ # Upload Images to ImgBB -A small PowerShell hobby script for uploading JPG, JPEG, and PNG files from a local directory to ImgBB through its HTTPS API. +A small PowerShell hobby script for uploading one JPG, JPEG, or PNG file, or matching files from a local directory, to ImgBB through its HTTPS API. Uploads run sequentially, with random delays enabled by default and bounded retries to reduce server load. Ordinary delays can be disabled explicitly; retry waits remain enabled. There is no configured limit on the number of files in a run. Service limits, available memory, network conditions, and processing time still apply. ## Requirements - Windows PowerShell 5.1 or PowerShell 7. The implementation targets both versions; runtime compatibility testing has not yet been completed. -- A readable local filesystem directory containing images. +- A readable local image file or filesystem directory containing images. - An ImgBB API key, including for dry runs. - Internet access to `https://api.imgbb.com/1/upload` for actual uploads. @@ -43,15 +43,18 @@ The examples use placeholders, not working credentials. Typing a real key into a | Parameter | Type | Default | Behavior | | --- | --- | --- | --- | -| `-DirectoryPath` | String | Required | Existing filesystem directory, interpreted literally. Relative paths are resolved from the current working directory. | +| `-DirectoryPath` | String | Unset | Existing filesystem directory, required unless `-FilePath` is supplied. Ignored when `-FilePath` is supplied. | +| `-FilePath` | String | Unset | One existing JPG/JPEG/PNG file. Takes precedence over `-DirectoryPath`. Paths are literal; relative paths use the current working directory. | | `-ApiKey` | String | Unset | A nonblank value takes precedence over `IMGBB_API_KEY`. | -| `-Recurse` | Switch | Off | Include subdirectories. | +| `-Recurse` | Switch | Off | Include subdirectories in directory mode. Ignored in single-file mode. | | `-WaitTime` | String | `Random` | Only `Off` disables the ordinary 3–6 second request delay. Missing, empty, or unrecognized values retain random waiting. Case-insensitive. Retry waits remain enabled. | | `-Silent` | Switch | Off | Print errors and skips only. Suppress progress, success URLs, retry notices, and the summary. | | `-DryRun` | Switch | Off | Perform local preparation without sending requests or waiting between files. | Switches can be combined. For example, `-Recurse -Silent -DryRun` checks the directory tree and displays only problems. +Supply `-FilePath` or `-DirectoryPath`. If both are present, **only the file is processed**; the directory is not inspected or validated. An explicitly supplied but empty, missing, inaccessible, or unsupported file path produces an error and exit code `1`; it never falls back to uploading a directory. If neither input is supplied, the script exits with an error. Input validation occurs after the API-key presence check. + `-WaitTime` intentionally accepts arbitrary string values and falls back to `Random` unless the value is exactly `Off`, ignoring letter case. Thus `-WaitTime 'invalid'`, `-WaitTime ''`, and omitting the parameter all keep random waiting. Supply a value when writing `-WaitTime`; a bare parameter without a value remains a PowerShell argument-binding error. A misspelled parameter name is also a binding error. If `-ApiKey` is omitted, empty, or whitespace, the script uses `IMGBB_API_KEY`. If neither supplies a nonblank key, the script exits with an error before scanning files. @@ -60,6 +63,26 @@ The standard PowerShell common parameters are also available, but `-WhatIf` and ## Examples +Upload one file: + +```powershell +.\Upload-ImagesToImgBB.ps1 -FilePath 'C:\Images\photo.jpg' +``` + +Preview one file without uploading: + +```powershell +.\Upload-ImagesToImgBB.ps1 -FilePath 'C:\Images\photo.jpg' -DryRun +``` + +When both paths are supplied, upload only the file: + +```powershell +.\Upload-ImagesToImgBB.ps1 -FilePath 'C:\Images\photo.jpg' -DirectoryPath 'C:\OtherImages' -Recurse +``` + +In this example, `C:\OtherImages` and `-Recurse` are ignored. `-ApiKey`, `-Silent`, `-DryRun`, and `-WaitTime` work in both modes. A successful single-file upload has no ordinary inter-request delay because it needs only one request; retries still follow the configured delay and backoff rules. + Include subdirectories: ```powershell @@ -114,9 +137,9 @@ Get-Help .\Upload-ImagesToImgBB.ps1 -Full ## File selection and preparation - Matches `.jpg`, `.jpeg`, and `.png` extensions, case-insensitively. -- Processes only the selected directory unless `-Recurse` is present. +- With `-FilePath`, processes only the selected file. Otherwise, processes only the selected directory unless `-Recurse` is present. - Does not request hidden files through `-Force`. -- Accepts brackets and other wildcard characters in directory names as literal characters. +- Accepts brackets and other wildcard characters in input paths as literal characters; wildcard expansion is not supported. - Requires a nonempty file no larger than **32 MiB (33,554,432 bytes)**. This is the script's local check; it does not guarantee that the service accepts a file. - Checks size before and after reading, then converts the file to Base64. - Does not decode images or verify that file contents match their extensions. @@ -171,7 +194,7 @@ Ordinary messages use `Write-Host`. Error and skip messages use plain .NET stand An empty, oversized, unreadable, or unencodable image produces a skip message. The file counts as failed, and processing continues. Size failures report the byte count; other preparation failures report the underlying exception type without dumping the exception or image contents. -A missing/inaccessible directory, enumeration failure, access-denied API status, or exhausted transient retries stops the run. A recursive scan can stop if it encounters an inaccessible directory. Files already uploaded remain uploaded. +An invalid selected input, enumeration failure, access-denied API status, or exhausted transient retries stops the run. A recursive scan can stop if it encounters an inaccessible directory. Files already uploaded remain uploaded. In single-file mode, local preparation failures still produce the same skip message and failed count; there is simply no next file to process. The normal summary contains `processed`, `uploaded`, and `failed` counts. In dry-run mode, `ready` replaces `uploaded`. A stopped run counts only files reached before the stop; it does not count the unvisited remainder. Some configuration failures occur before a summary can be printed. @@ -194,6 +217,8 @@ Invoke the script normally or in a separate PowerShell process; do not dot-sourc | Symptom | What to check | | --- | --- | | API key missing | Set `IMGBB_API_KEY` in the same session or provide `-ApiKey`. Dry runs also require a key. | +| FilePath input error | Check that the selected path is an accessible JPG/JPEG/PNG file. If `-FilePath` is supplied, the directory is ignored even when the file input is invalid. | +| Missing input | Supply `-FilePath` or `-DirectoryPath`. | | No matching files | Check the path, extensions, hidden attributes, and whether `-Recurse` is needed. | | Size skip | Confirm the file is nonempty and at or below the script's 32 MiB threshold. | | Cannot read or encode | Check the reported exception type, file permissions, availability, locks, and memory. | @@ -227,6 +252,7 @@ Before relying on it for a large batch: 1. Run a dry run on a small directory, including an empty file and a nested directory. 2. Confirm that `-Recurse`, `-Silent`, skip messages, colors, and exit codes behave as expected in your terminal. In a mocked upload test, verify that `-WaitTime Off` skips only ordinary delays, while omitted/invalid values retain them and retry waits remain active. + Also check single-file mode, file-over-directory precedence, and invalid file input without directory fallback. 3. Upload a small, non-sensitive test image and check the returned URL. 4. Validate retry and failure paths using mocks or a controlled test harness rather than deliberately overloading the live service.