From f0d2767bce00d420afc719337cb73522b8beebd7 Mon Sep 17 00:00:00 2001 From: blance Date: Mon, 29 Jun 2026 19:28:44 -0500 Subject: [PATCH] =?UTF-8?q?Initial=20upload=20script=20=E2=80=94=20PDF=20t?= =?UTF-8?q?o=20FTP=20with=20archive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++ README.md | 29 ++++++++++++++-- ftp-config.ini | 9 +++++ upload.bat | 3 ++ upload.ps1 | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 ftp-config.ini create mode 100644 upload.bat create mode 100644 upload.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b018f00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Never commit real credentials +ftp-config.ini +archive/ +*.pdf diff --git a/README.md b/README.md index dc87b18..fa4da0a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,28 @@ -# ftp-uploader +# FTP Uploader -Simple click-to-run FTP upload script for Windows \ No newline at end of file +Drop PDFs in a folder, double-click `upload.bat` to send them to FTP, done. +Uploaded files are moved to an `archive` subfolder automatically. + +## Setup (per practice folder) + +1. Copy `upload.bat`, `upload.ps1`, and `ftp-config.ini` into the folder +2. Edit `ftp-config.ini` with the FTP credentials for that practice +3. Drop PDFs into the folder +4. Double-click `upload.bat` + +## ftp-config.ini options + +| Key | Required | Description | +|-----|----------|-------------| +| `host` | Yes | FTP server hostname or IP | +| `port` | No | Port (default: 21) | +| `username` | Yes | FTP username | +| `password` | Yes | FTP password | +| `remote_path` | No | Remote folder path (default: `/`) | +| `tls` | No | Set to `true` for FTPS (default: `false`) | + +## Behaviour + +- Only `.pdf` files in the folder are uploaded (not subfolders) +- Files that fail are left in place for the next run to retry +- Successfully uploaded files move to `archive\` diff --git a/ftp-config.ini b/ftp-config.ini new file mode 100644 index 0000000..5d0fbca --- /dev/null +++ b/ftp-config.ini @@ -0,0 +1,9 @@ +# FTP configuration for this practice folder. +# Fill in the details below and keep this file private. + +host=ftp.example.com +port=21 +username=youruser +password=yourpassword +remote_path=/incoming +tls=false diff --git a/upload.bat b/upload.bat new file mode 100644 index 0000000..71e5c03 --- /dev/null +++ b/upload.bat @@ -0,0 +1,3 @@ +@echo off +powershell.exe -ExecutionPolicy Bypass -File "%~dp0upload.ps1" +pause diff --git a/upload.ps1 b/upload.ps1 new file mode 100644 index 0000000..6a4ff56 --- /dev/null +++ b/upload.ps1 @@ -0,0 +1,93 @@ +# FTP/FTPS PDF Uploader +# Place this script and ftp-config.ini in the same folder as your PDFs. +# Run via upload.bat (double-click). + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$configFile = Join-Path $scriptDir "ftp-config.ini" + +if (-not (Test-Path $configFile)) { + Write-Host "ERROR: ftp-config.ini not found." -ForegroundColor Red + Write-Host "Create ftp-config.ini in the same folder as this script." -ForegroundColor Red + exit 1 +} + +# Parse config +$config = @{} +foreach ($line in Get-Content $configFile) { + if ($line -match '^\s*([^=;#\s]+)\s*=\s*(.+?)\s*$') { + $config[$matches[1]] = $matches[2] + } +} + +$ftpHost = $config["host"] +$ftpPort = if ($config["port"]) { [int]$config["port"] } else { 21 } +$username = $config["username"] +$password = $config["password"] +$remotePath = if ($config["remote_path"]) { $config["remote_path"].TrimEnd("/") } else { "/" } +$useTls = $config["tls"] -eq "true" + +if (-not $ftpHost -or -not $username -or -not $password) { + Write-Host "ERROR: ftp-config.ini is missing required fields (host, username, password)." -ForegroundColor Red + exit 1 +} + +$archiveDir = Join-Path $scriptDir "archive" +if (-not (Test-Path $archiveDir)) { + New-Item -ItemType Directory -Path $archiveDir | Out-Null +} + +$pdfs = Get-ChildItem -Path $scriptDir -Filter "*.pdf" -File + +if ($pdfs.Count -eq 0) { + Write-Host "No PDF files found in this folder." -ForegroundColor Yellow + exit 0 +} + +$protocol = if ($useTls) { "ftps" } else { "ftp" } +Write-Host "" +Write-Host "Uploading $($pdfs.Count) PDF(s) to $protocol://$ftpHost$remotePath" -ForegroundColor Cyan +Write-Host "" + +$ok = 0 +$fail = 0 + +foreach ($pdf in $pdfs) { + $remoteFile = "$remotePath/$($pdf.Name)" + $uri = "ftp://${ftpHost}:${ftpPort}${remoteFile}" + + Write-Host " $($pdf.Name) ..." -NoNewline + + try { + $req = [System.Net.FtpWebRequest]::Create($uri) + $req.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile + $req.Credentials = New-Object System.Net.NetworkCredential($username, $password) + $req.EnableSsl = $useTls + $req.UseBinary = $true + $req.UsePassive = $true + $req.KeepAlive = $false + + $bytes = [System.IO.File]::ReadAllBytes($pdf.FullName) + $req.ContentLength = $bytes.Length + $stream = $req.GetRequestStream() + $stream.Write($bytes, 0, $bytes.Length) + $stream.Close() + + $resp = $req.GetResponse() + $resp.Close() + + Move-Item -Path $pdf.FullName -Destination (Join-Path $archiveDir $pdf.Name) -Force + Write-Host " uploaded" -ForegroundColor Green + $ok++ + } + catch { + Write-Host " FAILED: $($_.Exception.Message)" -ForegroundColor Red + $fail++ + } +} + +Write-Host "" +if ($fail -eq 0) { + Write-Host "Done: $ok file(s) uploaded and archived." -ForegroundColor Green +} else { + Write-Host "Done: $ok uploaded, $fail failed (left in place for retry)." -ForegroundColor Yellow +}