deploy: 修正FTP上传目标为/wwwroot并增强上传健壮性

- RemoteRoot 固定为 /wwwroot (IIS 站点物理根), 网页必须存放于 wwwroot 才可访问
- ftp_upload.ps1: 每文件独立连接、上传后校验大小、3次重试、日志记录
- 新增 ftp_fast.ps1 (快速覆盖上传) 与 ftp_sync.ps1 (仅补传缺失/大小不符文件)
- 上传前删除服务商默认欢迎页 default.html, 避免优先于 index.html
This commit is contained in:
2026-08-24 00:54:09 +08:00
parent 1bfc72f9cb
commit 2e3f68472e
4 changed files with 164 additions and 38 deletions
+31
View File
@@ -0,0 +1,31 @@
# Minimal direct upload (no size check) - fast overwrite upload
$FtpHost="116.198.221.125"; $FtpUser="wenchuanyi"; $FtpPass="r7P^f*v7rFts"
$Stage = Join-Path $env:TEMP "wcy_publish_staging"
$Log = "D:\CodeBuddy\Pros\WenChuanyi\deploy\ftp_fast.log"
Set-Content -Path $Log -Value "start $(Get-Date -Format HH:mm:ss)" -Encoding UTF8
function L([string]$m){ Add-Content -Path $Log -Value $m -Encoding UTF8 }
function NewR($p,$m){ $r=[System.Net.FtpWebRequest]::Create("ftp://${FtpHost}/$p"); $r.Method=$m; $r.Credentials=New-Object System.Net.NetworkCredential($FtpUser,$FtpPass); $r.UsePassive=$true; $r.UseBinary=$true; $r.KeepAlive=$false; $r.Timeout=120000; $r }
function Mk($p){ $r=NewR $p ([System.Net.WebRequestMethods+Ftp]::MakeDirectory); try{$null=$r.GetResponse()}catch{} }
function Put($lp,$rp){
for($i=1;$i -le 3;$i++){
try{
$r=NewR $rp ([System.Net.WebRequestMethods+Ftp]::UploadFile)
$b=[IO.File]::ReadAllBytes($lp)
$r.ContentLength=$b.Length
$s=$r.GetRequestStream(); $s.Write($b,0,$b.Length); $s.Close()
$x=$r.GetResponse(); $x.Close()
L "OK $rp"; return
}catch{ L "RETRY($i) $rp : $($_.Exception.Message)"; Start-Sleep 3 }
}
L "FAIL $rp"
}
function Up($ld,$rd){
foreach($d in Get-ChildItem $ld -Directory){
# skip Kerberos native dir - rejected by server, not needed on Windows
if($d.FullName -match "win-x64[\\/]native$"){ L "SKIPDIR $($d.FullName)"; continue }
$c="$rd/$($d.Name)"; Mk $c; Up $d.FullName $c
}
foreach($f in Get-ChildItem $ld -File){ Put $f.FullName "$rd/$($f.Name)" }
}
Up $Stage "/wwwroot"
L "DONE $(Get-Date -Format HH:mm:ss)"
+70
View File
@@ -0,0 +1,70 @@
# Sync-only upload: upload files that are missing or size-mismatched on FTP
$ErrorActionPreference = "Continue"
$FtpHost="116.198.221.125"; $FtpPort=21; $FtpUser="wenchuanyi"; $FtpPass="r7P^f*v7rFts"
$RemoteRoot="/wwwroot"
$Stage = Join-Path $env:TEMP "wcy_publish_staging"
$Log = "D:\CodeBuddy\Pros\WenChuanyi\deploy\ftp_sync.log"
$fail = $false
Set-Content -Path $Log -Value "=== sync start $(Get-Date -Format 'HH:mm:ss') ===" -Encoding UTF8
function L([string]$m){ Add-Content -Path $Log -Value $m -Encoding UTF8 }
function New-FtpReq([string]$path,[string]$method,[int]$timeoutMs=90000){
$uri="ftp://${FtpHost}:${FtpPort}/"+$path.TrimStart('/')
$r=[System.Net.FtpWebRequest]::Create($uri)
$r.Method=$method
$r.Credentials=New-Object System.Net.NetworkCredential($FtpUser,$FtpPass)
$r.UsePassive=$true; $r.UseBinary=$true; $r.KeepAlive=$false
$r.Timeout=$timeoutMs
return $r
}
function Get-FtpSize([string]$p){
for($i=0;$i -lt 3;$i++){
try{
$r=New-FtpReq $p ([System.Net.WebRequestMethods+Ftp]::GetFileSize) 30000
$resp=$r.GetResponse(); $s=$resp.ContentLength; $resp.Close()
return $s
}catch{ Start-Sleep -Seconds 2 }
}
return -1
}
function UpFile([string]$lp,[string]$rp){
$localSize=(Get-Item $lp).Length
$remoteSize=Get-FtpSize $rp
if($remoteSize -eq $localSize){ L "SKIP $rp"; return }
L "UPLOAD $rp (remote=$remoteSize local=$localSize)"
for($i=1;$i -le 3;$i++){
try{
$r=New-FtpReq $rp ([System.Net.WebRequestMethods+Ftp]::UploadFile) 180000
$b=[System.IO.File]::ReadAllBytes($lp)
$r.ContentLength=$b.Length
$s=$r.GetRequestStream()
try{ $s.Write($b,0,$b.Length) } finally { $s.Close() }
$resp=$r.GetResponse(); try{ $resp.Close() }catch{}
Start-Sleep -Seconds 1
$after=Get-FtpSize $rp
if($after -eq $localSize){ L "OK $rp"; return }
L "VERIFYFAIL $rp (got $after)"; continue
}catch{
L "RETRY($i) $rp : $($_.Exception.Message)"
Start-Sleep -Seconds 5
}
}
L "FAIL $rp"
$script:fail=$true
}
function UpDir([string]$ld,[string]$rd){
foreach($d in Get-ChildItem $ld -Directory){
$c="$rd/$($d.Name)"
$mr=New-FtpReq $c ([System.Net.WebRequestMethods+Ftp]::MakeDirectory) 30000
try{ $null=$mr.GetResponse() }catch{}
UpDir $d.FullName $c
}
foreach($f in Get-ChildItem $ld -File){
UpFile $f.FullName "$rd/$($f.Name)"
}
}
if(-not (Test-Path $Stage)){ L "STAGING_MISSING"; exit 1 }
UpDir $Stage $RemoteRoot
if($fail){ L "RESULT=PARTIAL_FAIL" } else { L "RESULT=OK" }
L "=== sync end $(Get-Date -Format 'HH:mm:ss') ==="
+46 -32
View File
@@ -1,59 +1,73 @@
# FTP 上传脚本 (UTF-8 BOM) - 健壮版 # Robust FTP upload: per-file connection, size-check skip, retry
$FtpHost="116.198.221.125" $ErrorActionPreference = "Continue"
$FtpPort=21 [System.Net.ServicePointManager]::DefaultConnectionLimit = 64
$FtpUser="wenchuanyi" $FtpHost="116.198.221.125"; $FtpPort=21; $FtpUser="wenchuanyi"; $FtpPass="r7P^f*v7rFts"
$FtpPass="r7P^f*v7rFts" $RemoteRoot="/wwwroot"
$RemoteRoot="/"
$Stage = Join-Path $env:TEMP "wcy_publish_staging" $Stage = Join-Path $env:TEMP "wcy_publish_staging"
$Log = "D:\CodeBuddy\Pros\WenChuanyi\deploy\ftp_upload.log"
$fail = $false
function New-FtpReq([string]$path,[string]$method){ function L([string]$m){ Add-Content -Path $Log -Value $m -Encoding UTF8 }
function New-FtpReq([string]$path,[string]$method,[int]$timeoutMs=120000){
$uri="ftp://${FtpHost}:${FtpPort}/"+$path.TrimStart('/') $uri="ftp://${FtpHost}:${FtpPort}/"+$path.TrimStart('/')
$r=[System.Net.FtpWebRequest]::Create($uri) $r=[System.Net.FtpWebRequest]::Create($uri)
$r.Method=$method $r.Method=$method
$r.Credentials=New-Object System.Net.NetworkCredential($FtpUser,$FtpPass) $r.Credentials=New-Object System.Net.NetworkCredential($FtpUser,$FtpPass)
$r.UsePassive=$true $r.UsePassive=$true; $r.UseBinary=$true; $r.KeepAlive=$false
$r.UseBinary=$true $r.Timeout=$timeoutMs
$r.KeepAlive=$false
$r.Timeout=120000
return $r return $r
} }
function MkDir([string]$p){ function Get-FtpSize([string]$p){
$r=New-FtpReq $p ([System.Net.WebRequestMethods+Ftp]::MakeDirectory) $r=New-FtpReq $p ([System.Net.WebRequestMethods+Ftp]::GetFileSize) 30000
try{ $null=$r.GetResponse() }catch{} try{ $resp=$r.GetResponse(); $s=$resp.ContentLength; $resp.Close(); return $s }catch{ return -1 }
}
function Exist([string]$p){
$r=New-FtpReq $p ([System.Net.WebRequestMethods+Ftp]::GetDateTimestamp)
try{ $null=$r.GetResponse(); return $true }catch{ return $false }
} }
function UpFile([string]$lp,[string]$rp){ function UpFile([string]$lp,[string]$rp){
$r=New-FtpReq $rp ([System.Net.WebRequestMethods+Ftp]::UploadFile) $localSize=(Get-Item $lp).Length
$remoteSize=Get-FtpSize $rp
if($remoteSize -eq $localSize){ L "SKIP $rp (size ok)"; return $true }
for($i=1;$i -le 3;$i++){
try{
$r=New-FtpReq $rp ([System.Net.WebRequestMethods+Ftp]::UploadFile) 240000
$b=[System.IO.File]::ReadAllBytes($lp) $b=[System.IO.File]::ReadAllBytes($lp)
$r.ContentLength=$b.Length $r.ContentLength=$b.Length
$s=$r.GetRequestStream() $s=$r.GetRequestStream()
try{ $s.Write($b,0,$b.Length) }finally{ $s.Close() } try{ $s.Write($b,0,$b.Length) } finally { $s.Close() }
$resp=$r.GetResponse() $resp=$r.GetResponse()
try{ $resp.Close() }catch{} try{ $resp.Close() }catch{}
$after=Get-FtpSize $rp
if($after -eq $localSize){ L "UP $rp"; return $true }
else{ L "VERIFYFAIL $rp (got $after want $localSize)"; continue }
}catch{
L "RETRY($i) $rp : $($_.Exception.Message)"
Start-Sleep -Seconds 3
}
}
L "FAIL $rp"
$script:fail=$true
return $false
} }
function UpDir([string]$ld,[string]$rd){ function UpDir([string]$ld,[string]$rd){
foreach($d in Get-ChildItem $ld -Directory){ foreach($d in Get-ChildItem $ld -Directory){
$c="$rd/$($d.Name)" $c="$rd/$($d.Name)"
if(-not (Exist $c)){ MkDir $c } $mr=New-FtpReq $c ([System.Net.WebRequestMethods+Ftp]::MakeDirectory) 30000
try{ $null=$mr.GetResponse() }catch{}
UpDir $d.FullName $c UpDir $d.FullName $c
} }
foreach($f in Get-ChildItem $ld -File){ foreach($f in Get-ChildItem $ld -File){
$rf="$rd/$($f.Name)" $rf="$rd/$($f.Name)"
try{ [void](UpFile $f.FullName $rf)
if(-not (Exist $rf)){ UpFile $f.FullName $rf; Write-Host "UP $rf" }
else { Write-Host "SKIP(exists) $rf" }
}catch{
Write-Host "RETRY $rf"
try{ UpFile $f.FullName $rf; Write-Host "OK $rf" }catch{ Write-Host "FAIL $rf : $_" }
}
} }
} }
if(-not (Test-Path $Stage)){ Write-Host "STAGING_MISSING"; exit 1 } Set-Content -Path $Log -Value "=== upload start $(Get-Date -Format 'HH:mm:ss') ===" -Encoding UTF8
if(-not (Exist $RemoteRoot)){ MkDir $RemoteRoot } if(-not (Test-Path $Stage)){ L "STAGING_MISSING"; exit 1 }
# delete welcome page first
$dr=New-FtpReq "/wwwroot/default.html" ([System.Net.WebRequestMethods+Ftp]::DeleteFile) 30000
try{ $null=$dr.GetResponse(); L "DELETED default.html" }catch{ L "default.html not present" }
$mr=New-FtpReq $RemoteRoot ([System.Net.WebRequestMethods+Ftp]::MakeDirectory) 30000
try{ $null=$mr.GetResponse() }catch{}
UpDir $Stage $RemoteRoot UpDir $Stage $RemoteRoot
Write-Host "FTP_UPLOAD_DONE" if($fail){ L "RESULT=PARTIAL_FAIL" } else { L "RESULT=OK" }
Remove-Item $Stage -Recurse -Force L "=== upload end ==="
+12 -1
View File
@@ -4,6 +4,9 @@
# 2. npm run build 前端 (Vite, dist) # 2. npm run build 前端 (Vite, dist)
# 3. FTP 上传合并产物至 IIS 站点根目录 # 3. FTP 上传合并产物至 IIS 站点根目录
# #
# 重要: 官方提示 —— 网页必需存放在 wwwroot 目录下才能被访问!
# 因此 RemoteRoot 固定为 /wwwroot (FTP 上的目录, 即 IIS 站点物理根)
#
# 用法: # 用法:
# powershell -ExecutionPolicy Bypass -File deploy/publish.ps1 # powershell -ExecutionPolicy Bypass -File deploy/publish.ps1
# #
@@ -14,7 +17,7 @@ param(
[int] $FtpPort = 21, [int] $FtpPort = 21,
[string]$FtpUser = "wenchuanyi", [string]$FtpUser = "wenchuanyi",
[string]$FtpPass = "r7P^f*v7rFts", [string]$FtpPass = "r7P^f*v7rFts",
[string]$RemoteRoot = "/", # FTP 远端目标目录 (IIS 站点根目录) [string]$RemoteRoot = "/wwwroot", # FTP 远端目标目录 = IIS 站点根目录 (勿改!)
[switch]$SkipBuild # 跳过构建, 仅上传已有产物 [switch]$SkipBuild # 跳过构建, 仅上传已有产物
) )
@@ -106,6 +109,14 @@ function Send-FtpDirectory([string]$localDir, [string]$remoteDir) {
} }
# ---------------- 上传 ---------------- # ---------------- 上传 ----------------
# 删除服务商默认欢迎页 default.html, 避免 IIS 默认文档优先于 index.html
function Remove-FtpFile([string]$remotePath) {
$req = New-FtpRequest $remotePath ([System.Net.WebRequestMethods+Ftp]::DeleteFile)
try { $null = $req.GetResponse(); Write-Host " DEL $remotePath" -ForegroundColor DarkGray }
catch { }
}
Remove-FtpFile "$RemoteRoot/default.html"
New-FtpDirectory $RemoteRoot New-FtpDirectory $RemoteRoot
Send-FtpDirectory $StagingDir $RemoteRoot Send-FtpDirectory $StagingDir $RemoteRoot