# 文传易 · IIS 部署与 FTP 发布手册 > 目标环境:Windows Server + IIS 10 + .NET 8 Hosting Bundle > 站点地址:`https://wenchuanyi.bbitcn.net` > 发布方式:FTP(`ftp://116.198.221.125`,端口 21,用户 `wenchuanyi`,密码同数据库密码) > **本文档已按 2026-08-24 线上实战排错结果校正,照此执行可避免再踩坑。** --- ## 0. 目录结构(最重要的认知,务必先看) IIS 站点物理根 = **ContentRoot**(应用工作目录),也是后端产物所在目录; ASP.NET Core 的静态文件 WebRoot = **ContentRoot\wwwroot(嵌套)**。 实测服务器目录映射(FTP 的 `/wwwroot` ↔ 磁盘 `D:\wwwroot\wenchuanyi\wwwroot`): ``` D:\wwwroot\wenchuanyi\wwwroot\ ← FTP /wwwroot (ContentRoot,后端产物 + web.config) ├── web.config ← inprocess + dotnet(见第 3 节) ├── WenChuanyi.Api.dll / .exe / deps.json / runtimeconfig.json ├── appsettings.json ← 数据库 localhost / OSS / 上传限制 ├── *.dll(MySql.Data / BouncyCastle / ZstdSharp …) ← 缺一不可! └── wwwroot\ ← FTP /wwwroot/wwwroot (嵌套 WebRoot,前端产物!) ├── index.html └── assets\index-*.js / *.css ``` > **规则:后端产物传 `/wwwroot/`,前端产物传 `/wwwroot/wwwroot/`(嵌套)。** > 前端产物若只放站点根(`/wwwroot/`),页面能 200(IIS 默认文档/fallback)但 `/assets/*.js` 必然 404。 --- ## 1. 部署架构 ``` 浏览器 → https://wenchuanyi.bbitcn.net ▼ IIS + ANCM(AspNetCoreModuleV2,InProcess) ├── /assets/*、/ → ASP.NET Core UseDefaultFiles+UseStaticFiles(只读嵌套 WebRoot) ├── /#/pickup 等 SPA → MapFallbackToFile("index.html") 兜底(hash 路由) └── /api、/api/open → 后端控制器 数据库:MySQL 同机 localhost:3306(库/用户 wenchuanyi) 存储:阿里云 OSS(凭据在 appsettings.json,文件不入库) ``` --- ## 2. 服务器一次性准备(首次部署前) 1. **安装 .NET 8 Hosting Bundle**(dotnet.microsoft.com/download/dotnet/8.0)。 2. **创建 IIS 站点**:物理路径指向 `D:\wwwroot\wenchuanyi\wwwroot`(后端产物目录); 应用程序池 .NET CLR 版本选「**无托管代码**」(InProcess 由 ANCM 托管)。 3. **HTTPS 证书**:为 `wenchuanyi.bbitcn.net` 绑定 SSL 证书。 4. **防火墙**:放行 80 / 443(测试期 5280)。 --- ## 3. web.config(不要乱改) `dotnet publish` 自动生成的**默认配置**就是正确配置,保持原样: ```xml ``` - ✅ **必须 `inprocess + dotnet`**(Framework-dependent 标准)。 - ❌ 不要改成 `outofprocess + exe`——实测会导致 502/runtime 探测失败。 - ✅ `stdoutLogEnabled` 平时为 false;排障时才临时开 true,**查完必须改回 false**(改 web.config 会触发应用重启)。 --- ## 4. 构建发布包(开发机) ```powershell # 后端 cd backend/WenChuanyi.Api dotnet publish -c Release # 输出 bin/Release/net8.0/publish/ # 前端 cd frontend npm run build # 输出 dist/(index.html + assets/) ``` `publish/` 目录**所有文件**都要上传(约 24 个文件 / 12.4MB),框架依赖模式**不含** coreclr.dll 等 runtime 文件。 --- ## 5. 敏感配置(appsettings.json,不入库) | 配置节 | 键 | 值 | | --- | --- | --- | | `ConnectionStrings:MySql` | Data Source | **`localhost`**(MySQL 与站点同机,勿用公网 IP 回环) | | `ConnectionStrings:MySql` | Password | `r7P^f*v7rFts` | | `Oss` | AccessKeyId / Secret | 阿里云 OSS 凭据 | | `App` | BaseUrl | `https://wenchuanyi.bbitcn.net` | 改连接串后**必须重启应用**(上传 web.config 或回收应用池)才生效——FreeSql 连接字符串启动时固定。 --- ## 6. FTP 发布 ### 6.1 上传清单(位置错了就会出上面的 404/500) ``` 后端 publish/ 全部文件(web.config、appsettings.json、*.dll、*.exe、runtimes/) → FTP /wwwroot/ 前端 dist/index.html + assets/ 全部 → FTP /wwwroot/wwwroot/ ``` ### 6.2 脚本 - `deploy/ftp_upload.ps1`:完整发布(后端 publish + 前端 build + FTP 递归上传)。 - `deploy/ftp_fast.ps1`:快速覆盖上传当前产物。 - `deploy/ftp_sync.ps1`:只补传缺失/大小不符的文件(推荐增量修复)。 - `deploy/ftp_apply.ps1`:定向更新(appsettings / web.config / 嵌套 assets)。 ### 6.3 发布后生效 上传 **web.config** 或**回收应用程序池**使新版本生效: ```powershell C:\Windows\System32\inetsrv\appcmd recycle apppool /apppool.name:wenchuanyi ``` > 无法远程回收时:重传一次 web.config(内容不变也行)即可触发 ANCM 重启。 --- ## 7. 发布后验证清单(照做) ```powershell # 1. 首页 curl.exe -s -o NUL -w "%{http_code}\n" https://wenchuanyi.bbitcn.net/ # → 200 # 2. JS / CSS 静态资源(最关键!之前 404 就出在这) curl.exe -s -o NUL -w "%{http_code} %{size_download}\n" https://wenchuanyi.bbitcn.net/assets/index-Bwz67ZeI.js # → 200 1558667 # 3. API(返回业务校验/JSON 而非 404 即后端存活) curl.exe -s https://wenchuanyi.bbitcn.net/api/files/by-tag/__probe__ # → {"message":"标签无效"} HTTP 400(正常业务响应) # 4. 全流程:上传 → 取件码 → 下载;管理列表可见 ``` --- ## 8. 故障排查速查(本次实战总结) | 现象 | 原因 | 处理 | | --- | --- | --- | | **500.30** app failed to start | ① publish 文件**漏传**(本次漏 `MySql.Data.dll`、`K4os.Compression.LZ4.dll`,FreeSql.MySql 运行必需);② 站点根混入外来 runtime 文件(coreclr/hostfxr/hostpolicy/aspnetcorev2_inprocess 等);③ 数据库连不上(启动时 `SyncStructure` 抛错) | 对比本地 `publish/` 与服务器文件清单;删掉外来 runtime 文件;开 stdout 看具体异常 | | **404 静态资源**(`/assets/*.js`) | 前端产物放错目录——只放站点根没放**嵌套 `wwwroot/wwwroot`** | 前端产物必须传 `/wwwroot/wwwroot/` | | **502 / outofprocess 探测失败** | web.config 被改成 `outofprocess + exe` | 恢复 `inprocess + dotnet` | | **FTP 上传 550** | 目标文件已存在且被 IIS 进程锁定;或服务器限流 | **先 DeleteFile 再上传**,或临时名上传 + Rename;串行 + 重试 | | **FTP 连接频繁中断** | 服务器对高频连接限流 | 所有 FTP 脚本已串行 + 每次间隔 + 校验大小重试 | | **改配置不生效** | appsettings/代码变了但没重启应用 | 重传 web.config 或回收应用池 | ### 排查 500.30 的标准流程 1. **开 stdout**:web.config 改 `stdoutLogEnabled="true"` → 等待重启 → 下载 `logs/stdout_*.log` 看异常 → 查完改回 false。 2. **比对文件**:本地 `publish/` 文件清单(名字+大小)vs FTP `/wwwroot/` 清单,找出缺失项。 3. **看目录结构**:确认前后端产物分别在 `/wwwroot/` 与 `/wwwroot/wwwroot/`,站点根无多余 runtime 文件。 --- ## 9. 安全提示 - `appsettings.json` 含数据库密码与 OSS 凭据,**不要上传到代码仓库 / 不要外传**(已在 .gitignore)。 - FTP 凭据仅运维持有,建议定期轮换。 - 日志不会打印 OSS 凭据、数据库密码与文件内容明文。