AI 摘要
AI
正在生成摘要...

1 前言

在前端开发工作流中,文件打包后上传服务器往往是一个繁琐且容易出错的环节。传统前端部署流程涉及 npm run build 构建、手动压缩文件、FTP 工具上传、服务器解压替换等重复性操作,这些操作不仅容易出错,还十分耗时耗力,完整流程平均耗时 5 - 10 分钟。这归根结底还是因为我嫌麻烦,所以为了实现解放双手(简化步骤),在网上查阅相关方案后,比较适合中小项目的主要有以下几种:

  • 使用 scp 命令将本地文件夹上传到服务器指定目录,配合 package.json 配置脚本,操作简单,但功能较为单一。
  • 服务器配置 Git 和 Webhook,本地将文件夹提交到 Git 仓库,服务器初始化仓库自动拉取,版本回溯方便,但前期配置较复杂易出错,不推荐。
  • 利用三方部署命令行工具,如 deploy-cli-service,利用 npm 命令安装,再进行简单配置后就可使用,功能强大全面,自动化程度高,比较推荐。
  • 基于 scp 命令自己编写 shell 或 bat 脚本,根据需要自定义增减功能,兼容性足够好,重要的是特别适合部分网络受限的单位。

以上方法各有优缺,任选其一都可以简化部署流程,而我选择的则是最后一种,即使用以 scp 为核心的一系列命令来编写一个 BAT 脚本,每次仅需要双击执行这个脚本就可以快速完成部署。

2 知识科普

在开始之前,有必要先来了解一下最基本的知识。

2.1 SCP 命令

SCP(Secure Copy Protocol)是基于SSH协议的安全文件传输命令,本质上是 rcp 命令的安全升级版,主要用于在本地主机与远程主机之间加密传输文件和目录。

cp 命令在传输上大多是 本地 → 本地 的方向,而 scp 命令则可以做到 本地 → 远程远程 → 本地远程 → 远程 三种传输方向。

BASH
# 本地 → 远程
scp /path/to/local/file user@remote_ip:/remote/path
# 远程 → 本地
scp user@remote_ip:/remote/path/file /local/path
# 远程 → 远程(借助本地中转)
scp user1@host1:/path/file user2@host2:/path/

常用参数

  • -r:递归复制整个目录。
  • -P:指定远程主机的 SSH 端口号(默认是 22)。
  • -p:保留文件的修改时间、访问时间和权限。
  • -v:显示详细的调试信息,有助于排查问题。
  • -C:启用压缩,可以加快传输速度。

2.2 BAT 脚本

批处理脚本(BAT)是一系列你给计算机的指令,文件通常以 .bat.cmd 作为后缀拓展名,可以在 Windows 系统上运行。

基本命令

  • @echo off

关闭命令提示符窗口中命令的回显,使脚本执行时只显示命令执行结果,而不显示具体命令内容,增强脚本执行的简洁性。

SHELL
@echo off
echo 这行内容会显示,但此 echo 命令本身不会显示
  • 注释

注释可以帮助理解脚本功能、逻辑和用途等,在执行时会被忽略。:: 的效率高于 rem,但不能在括号内使用。

SHELL
rem 这是注释
:: 这也是注释(更推荐)

变量操作

  • 定义变量
SHELL
set name=张三
  • 使用变量
SHELL
echo %name%
  • 输入变量
SHELL
set /p input=请输入内容:

流程控制

  • 条件判断
SHELL
if "%input%"=="y" (
  echo 你选择了是
) else (
  echo 你选择了否
)
  • 检查文件存在
SHELL
if exist "test.txt" echo 文件存在
  • 简单循环
SHELL
:loop
echo 循环中...
timeout /t 1 >nul
goto loop

文件操作

  • 创建目录
SHELL
mkdir new_folder
  • 删除文件
SHELL
del old.txt
  • 切换目录
SHELL
cd ./folder
cd ..

实用技巧

  • 暂停脚本
SHELL
pause
  • 显示当前路径
SHELL
echo 当前目录:%cd%

常用符号

  • > 输出到文件(覆盖)
SHELL
echo 内容 > log.txt
  • >> 追加到文件
SHELL
echo 新内容 >> log.txt
  • | 管道符
SHELL
dir | find "txt"  :: 查找包含"txt"的目录项

3 脚本编写

脚本编写过程中我会按照功能需求优先级来分布实现。

3.1 核心功能实现

首先创建一个脚本文件 upload.bat,然后使用任意文本编辑工具去打开它。

先来编写整个脚本最核心也是最基本的内容,打开一个命令行然后将你指定的文件夹上传到服务器指定目录。

SHELL
@echo off

set REMOTE_USER=your_username(远程服务器用户名)
set REMOTE_HOST=your_server_ip(远程服务器IP)
set REMOTE_DIR=/path/to/remote/directory(要上传到的远程服务器目录)
set LOCAL_DIR=.\dist(本地要上传的文件夹)

scp -r "%LOCAL_DIR%" %REMOTE_USER%@%REMOTE_HOST%:%REMOTE_DIR%

endlocal
  1. @echo off:关闭命令行的回显功能,避免在执行过程中显示每条命令。
  2. 设置远程服务器信息,需按照提示分别修改 REMOTE_USERREMOTE_HOSTREMOTE_DIRLOCAL_DIR 的值。
  3. scp -rscp 是用于安全复制文件和文件夹的命令,-r 选项表示递归复制,即复制整个文件夹及其子文件夹。
  4. endlocal:结束本地环境变量的设置。

image-20250427232950896

3.2 优化逻辑判断

使用两个 if 判断分别检查本地目录存在性和 SCP 执行结果,能够避免尝试上传不存在的目录和明确反馈操作结果,两次使用 pause 命令可以更有效地查看错误信息。

SHELL
@echo off

set REMOTE_USER=your_username(远程服务器用户名)
set REMOTE_HOST=your_server_ip(远程服务器IP)
set REMOTE_DIR=/path/to/remote/directory(要上传到的远程服务器目录)
set LOCAL_DIR=.\dist(本地要上传的文件夹)

:: 检查本地目录是否存在
if not exist "%LOCAL_DIR%" (
    echo 错误:本地目录 "%LOCAL_DIR%" 不存在
    pause
    exit /b 1
)

scp -r "%LOCAL_DIR%" %REMOTE_USER%@%REMOTE_HOST%:%REMOTE_DIR%

:: 检查上传结果
if %ERRORLEVEL% equ 0 (
    echo 文件上传成功
) else (
    echo 文件上传失败
)

pause
endlocal

image-20250427235552270

3.3 实现多项目功能

假设我们有多个项目需要上传文件夹部署,可以通过预设和手动输入来实现。预设用来配置经常需要上传的项目,而手动输入可以上传临时项目或文件夹。

SHELL
echo 请选择要执行的操作:
echo 1. 上传项目 1
echo 2. 上传项目 2
echo 3. 手动输入配置
:CHOICE
set /p CHOICE=请输入数字 (1, 2, 3): 

if "%CHOICE%"=="1" (
    set REMOTE_USER=your_username1
    set REMOTE_HOST=your_server_ip1
    set REMOTE_DIR=/path/to/remote/directory1
    set LOCAL_DIR=.\dist1
) else if "%CHOICE%"=="2" (
    set REMOTE_USER=your_username2
    set REMOTE_HOST=your_server_ip2
    set REMOTE_DIR=/path/to/remote/directory2
    set LOCAL_DIR=.\dist2
) else if "%CHOICE%"=="3" (
    set /p REMOTE_USER=请输入远程服务器用户名: 
    set /p REMOTE_HOST=请输入远程服务器 IP: 
    set /p REMOTE_DIR=请输入要上传到的远程服务器目录: 
    set /p LOCAL_DIR=请输入本地要上传的文件夹: 
) else (
    echo 无效的选择,请重新输入。
    goto CHOICE
)

输入数字 1 或 2 时,分别上传项目 1 或项目 2 的预设配置,输入数字 3 手动输入配置,需要输入服务器用户名、IP、目录以及本地文件夹等信息,若选择非 1-3 则需要重新输入。

image-20250428004943860

3.4 改进交互体验

使用视觉分隔区分不同信息类型,优化错误处理,结合提示语增强交互。

SHELL
@echo off
title 前端项目自动化上传工具

echo.
echo ╔══════════════════════════════════════════════╗
echo ║         前端项目自动化上传工具 v1.0          ║
echo ╚══════════════════════════════════════════════╝
echo.

echo 请选择要执行的操作:
echo 1. 上传项目 1
echo 2. 上传项目 2
echo 3. 手动输入配置
:CHOICE
set /p CHOICE=请输入数字 (1, 2, 3): 

if "%CHOICE%"=="1" (
    set REMOTE_USER=your_username1
    set REMOTE_HOST=your_server_ip1
    set REMOTE_DIR=/path/to/remote/directory1
    set LOCAL_DIR=.\dist1
) else if "%CHOICE%"=="2" (
    set REMOTE_USER=your_username2
    set REMOTE_HOST=your_server_ip2
    set REMOTE_DIR=/path/to/remote/directory2
    set LOCAL_DIR=.\dist2
) else if "%CHOICE%"=="3" (
    set /p REMOTE_USER=请输入远程服务器用户名: 
    set /p REMOTE_HOST=请输入远程服务器 IP: 
    set /p REMOTE_DIR=请输入要上传到的远程服务器目录: 
    set /p LOCAL_DIR=请输入本地要上传的文件夹: 
) else (
    echo 无效的选择,请重新输入。
    goto CHOICE
)

if not exist "%LOCAL_DIR%" (
    echo ████████████████████████████████████████████
    echo 错误:本地目录 "%LOCAL_DIR%" 不存在
    echo 请检查以下可能原因:
    echo 1. 项目未正确构建
    echo 2. 目录路径配置错误
    echo 3. 脚本运行位置不正确
    echo.
    echo 当前工作目录:%cd%
    echo ████████████████████████████████████████████
    pause
	exit /b 1
)

for /f "delims=" %%a in ('wmic os get localdatetime ^| find "."') do set start=%%a

echo ■ 准备上传目录: %LOCAL_DIR%
echo ■ 目标服务器: %REMOTE_USER%@%REMOTE_HOST%
echo ■ 远程路径: %REMOTE_DIR%
echo.
echo ※ 正在上传文件,请稍候...(首次连接需要确认指纹)

scp -r "%LOCAL_DIR%" %REMOTE_USER%@%REMOTE_HOST%:%REMOTE_DIR%

for /f "delims=" %%a in ('wmic os get localdatetime ^| find "."') do set end=%%a

set /a "duration=(1%end:~8,2%-100)*3600 + (1%end:~10,2%-100)*60 + (1%end:~12,2%-100)"
set /a "duration=duration - ((1%start:~8,2%-100)*3600 + (1%start:~10,2%-100)*60 + (1%start:~12,2%-100))"
if %duration% lss 0 set /a duration+=86400

echo.
if %ERRORLEVEL% equ 0 (
    echo ████████████████████████████████████████████
    echo ■ 上传成功!耗时 %duration% 秒
    echo ■ 传输完成时间: %date% %time%
    echo ████████████████████████████████████████████
) else (
    echo ████████████████████████████████████████████
    echo ■ 上传失败!错误代码: %ERRORLEVEL%
    echo.
    echo 可能原因:
    echo 1. 服务器连接失败
    echo 2. 认证失败(检查用户名/密码/密钥)
    echo 3. 远程目录不可写
    echo 4. 磁盘空间不足
    echo.
    echo 建议操作:
    echo 1. 检查网络连接
    echo 2. 验证服务器配置参数
    echo 3. 手动尝试SCP命令排错
    echo ████████████████████████████████████████████
)

echo.
echo 操作已完成,按任意键退出...
pause >nul
endlocal

image-20250428012252750

3.5 额外功能:解压缩

为减少数据传输量、提高传输稳定性,建议增加解压缩功能,还便于版本管理。

SHELL
:: 生成时间戳和压缩包名称
for /f "tokens=2 delims==" %%a in ('wmic os get localdatetime /value') do set datetime=%%a
set TIMESTAMP=%datetime:~0,4%-%datetime:~4,2%-%datetime:~6,2%_%datetime:~8,2%-%datetime:~10,2%-%datetime:~12,2%
set ZIP_NAME=deploy_%TIMESTAMP%.zip
set TEMP_ZIP=.\temp\%ZIP_NAME%

:: 第一步:本地压缩
echo ※ 正在压缩本地文件...
powershell -command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('%LOCAL_DIR%', '%TEMP_ZIP%')"

if not exist "%TEMP_ZIP%" (
    pause
    exit /b 1
)

echo √ 压缩完成,压缩包大小: 
for %%F in ("%TEMP_ZIP%") do echo    %%~zF 字节
echo.

:: 第二步:上传压缩包
echo ※ 正在上传压缩包,请稍候...(首次连接需要确认指纹)
scp "%TEMP_ZIP%" %REMOTE_USER%@%REMOTE_HOST%:%REMOTE_DIR%/

if not %ERRORLEVEL% equ 0 (
    del "%TEMP_ZIP%" >nul 2>&1
    pause
    exit /b 1
)

:: 第三步:服务器端解压
echo ※ 正在远程解压文件...
ssh %REMOTE_USER%@%REMOTE_HOST% "cd %REMOTE_DIR% && unzip -oq %ZIP_NAME%"

if not %ERRORLEVEL% equ 0 (
    del "%TEMP_ZIP%" >nul 2>&1
    pause
    exit /b 1
)

:: 清理临时文件
del "%TEMP_ZIP%" >nul 2>&1

3.6 进一步功能增强

接下来,我们可以结合 npm 构建命令,增强脚本自动化,从构建到部署一次性完成,还可以新增退出选项和循环执行,让脚本使用体验更流畅易用。

当然,可以整合的功能远不止此,但做到目前实现的功能对于个人来说完全够用,下面就展示一下完整的文件。

4 完整文件

SHELL
:: upload.bat

@echo off
title 前端项目自动化上传工具

:MAIN_LOOP
cls
echo.
echo ╔══════════════════════════════════════════════╗
echo ║         前端项目自动化上传工具 v1.0          ║
echo ╚══════════════════════════════════════════════╝
echo.

echo 请选择要执行的操作:
echo 1. 上传项目 1
echo 2. 上传项目 2
echo 3. 手动输入配置
echo 4. 退出程序
echo.

:CHOICE
set /p CHOICE=请输入数字 (1, 2, 3, 4): 

if "%CHOICE%"=="1" (
    set REMOTE_USER=your_username1(远程服务器用户名)
    set REMOTE_HOST=your_server_ip1(远程服务器IP)
    set REMOTE_DIR=/path/to/remote/directory1(要上传到的远程服务器目录)
    set PROJECT_DIR=.\project1(本地项目地址,该处执行npm构建命令)
    set NPM_COMMAND=npm run build(本地要执行的npm构建命令)
    set LOCAL_DIR=.\dist(本地要上传的文件夹,注意为项目地址的相对路径)
) else if "%CHOICE%"=="2" (
    set REMOTE_USER=your_username2
    set REMOTE_HOST=your_server_ip2
    set REMOTE_DIR=/path/to/remote/directory2
    set PROJECT_DIR=.\project1
    set NPM_COMMAND=npm run build
    set LOCAL_DIR=.\dist2
) else if "%CHOICE%"=="3" (
    set /p REMOTE_USER=请输入远程服务器用户名: 
    set /p REMOTE_HOST=请输入远程服务器 IP: 
    set /p REMOTE_DIR=请输入要上传到的远程服务器目录: 
    set /p PROJECT_DIR=请输入项目所在目录: 
    set /p NPM_COMMAND=请输入 npm 构建命令: 
    set /p LOCAL_DIR=请输入本地要上传的文件夹: 
) else if "%CHOICE%"=="4" (
    echo 正在退出程序...
    timeout /t 1 >nul
    exit /b 0
) else (
    echo 无效的选择,请重新输入。
    goto CHOICE
)

for /f "delims=" %%a in ('wmic os get localdatetime ^| find "."') do set start=%%a

if not exist "%PROJECT_DIR%" (
    echo ████████████████████████████████████████████
    echo 错误:本地目录 "%PROJECT_DIR%" 不存在
    echo 请检查以下可能原因:
    echo 1. 项目未正确构建
    echo 2. 目录路径配置错误
    echo 3. 脚本运行位置不正确
    echo.
    echo 当前工作目录:%cd%
    echo ████████████████████████████████████████████
    echo 按任意键返回主菜单...
    pause >nul
    cd /d %~dp0
    goto MAIN_LOOP
)

echo ■ 正在执行 npm 构建命令: %NPM_COMMAND%
echo ■ 请耐心等待
cd /d %PROJECT_DIR%
call %NPM_COMMAND% >nul 2>&1
if %ERRORLEVEL% neq 0 (
    echo ████████████████████████████████████████████
    echo ■ npm 构建失败!错误代码: %ERRORLEVEL%
    echo.
    echo 请检查以下可能原因:
    echo 1. 项目依赖未正确安装
    echo 2. 构建脚本配置错误
    echo.
    echo 建议操作:
    echo 1. 执行 npm install 安装依赖
    echo 2. 检查项目的 package.json 文件中的脚本配置
    echo ████████████████████████████████████████████
    echo 按任意键返回主菜单...
    pause >nul
    cd /d %~dp0
    goto MAIN_LOOP
)

for /f "tokens=2 delims==" %%a in ('wmic os get localdatetime /value') do set datetime=%%a
set TIMESTAMP=%datetime:~0,4%-%datetime:~4,2%-%datetime:~6,2%_%datetime:~8,2%-%datetime:~10,2%-%datetime:~12,2%
set ZIP_NAME=deploy_%TIMESTAMP%.zip
set TEMP_ZIP=.\temp\%ZIP_NAME%

if not exist ".\temp" (
    mkdir ".\temp"
    if %ERRORLEVEL% neq 0 (
        echo ████████████████████████████████████████████
        echo 错误:无法创建临时目录 ".\temp"
        echo 可能原因:
        echo 1. 磁盘空间不足
        echo 2. 文件权限问题
        echo ████████████████████████████████████████████
        echo 按任意键返回主菜单...
        pause >nul
        cd /d %~dp0
        goto MAIN_LOOP
    )
)

echo ■ 准备压缩: %LOCAL_DIR%
echo.

echo ※ 正在压缩本地文件...
powershell -command "Add-Type -Assembly 'System.IO.Compression.FileSystem'; [System.IO.Compression.ZipFile]::CreateFromDirectory('%LOCAL_DIR%', '%TEMP_ZIP%')"

if not exist "%TEMP_ZIP%" (
    echo ████████████████████████████████████████████
    echo 错误:压缩文件创建失败
    echo 可能原因:
    echo 1. 磁盘空间不足
    echo 2. 文件权限问题
    echo 3. PowerShell 执行失败
    echo ████████████████████████████████████████████
    echo 按任意键返回主菜单...
    pause >nul
    cd /d %~dp0
    goto MAIN_LOOP
)

echo √ 压缩完成,压缩包大小: 
for %%F in ("%TEMP_ZIP%") do echo    %%~zF 字节
echo.

echo ※ 正在上传压缩包,请稍候...(首次连接需要确认指纹)
echo ■ 准备上传压缩包: %TEMP_ZIP%
echo ■ 目标服务器: %REMOTE_USER%@%REMOTE_HOST%
echo ■ 远程路径: %REMOTE_DIR%
scp "%TEMP_ZIP%" %REMOTE_USER%@%REMOTE_HOST%:%REMOTE_DIR%/

if not %ERRORLEVEL% equ 0 (
    echo ████████████████████████████████████████████
    echo ■ 上传失败!错误代码: %ERRORLEVEL%
    echo.
    echo 可能原因:
    echo 1. 服务器连接失败
    echo 2. 认证失败(检查用户名/密码/密钥)
    echo 3. 远程目录不可写
    echo 4. 磁盘空间不足
    echo.
    echo 建议操作:
    echo 1. 检查网络连接
    echo 2. 验证服务器配置参数
    echo 3. 手动尝试SCP命令排错
    echo ████████████████████████████████████████████
    del "%TEMP_ZIP%" >nul 2>&1
    echo 按任意键返回主菜单...
    pause >nul
    cd /d %~dp0
    goto MAIN_LOOP
)

echo ※ 正在远程解压文件...
ssh %REMOTE_USER%@%REMOTE_HOST% "cd %REMOTE_DIR% && unzip -oq %ZIP_NAME%"

for /f "delims=" %%a in ('wmic os get localdatetime ^| find "."') do set end=%%a

set /a "duration=(1%end:~8,2%-100)*3600 + (1%end:~10,2%-100)*60 + (1%end:~12,2%-100)"
set /a "duration=duration - ((1%start:~8,2%-100)*3600 + (1%start:~10,2%-100)*60 + (1%start:~12,2%-100))"
if %duration% lss 0 set /a duration+=86400

del "%TEMP_ZIP%" >nul 2>&1

echo.
echo ████████████████████████████████████████████
echo √ 操作成功完成!耗时 %duration% 秒
echo ■ 传输完成时间: %date% %time%
echo ■ 已执行步骤:
echo   1. 本地执行 %NPM_COMMAND% 命令
echo   2. 本地压缩: %LOCAL_DIR% → %ZIP_NAME%
echo   3. 上传压缩包到: %REMOTE_DIR%/
echo   4. 服务器解压
echo ████████████████████████████████████████████

echo.
echo 按任意键返回主菜单...
pause >nul
cd /d %~dp0
goto MAIN_LOOP

5 解决免密登录

想要解决每次执行 scp 命令都要输入密码很简单,只需要设置 SSH 公钥认证,就可以使后续连接无需密码登录。它的实现方法流程为:本地生成 SSH 密钥对 > 将公钥上传到服务器 /root/.ssh/ 下 > 重命名为 authorized_keys

为了简化这一流程,我们也可以用一个 bat 文件来实现:

SHELL
@echo off
setlocal enabledelayedexpansion

:: 1. 生成 SSH 密钥对(如果不存在)
if not exist "%USERPROFILE%\.ssh\id_rsa" (
    echo 正在生成 SSH RSA 密钥对...
    ssh-keygen -t rsa -f "%USERPROFILE%\.ssh\id_rsa" -q -N ""
) else (
    echo 检测到已存在的 SSH 密钥,跳过生成步骤...
)

:: 2. 获取服务器信息
set /p server_ip=请输入服务器IP地址: 
set /p server_username=请输入服务器用户名(默认为root): 
if "!server_username!"=="" set server_username=root

:: 3. 创建远程.ssh目录(如果不存在)并上传公钥
echo 正在上传公钥到服务器...
type "%USERPROFILE%\.ssh\id_rsa.pub" | ssh !server_username!@!server_ip! "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

echo 公钥上传完成
pause

image-20250428170748672

6 写在最后

通过以上步骤,我们成功打造了一个功能强大且实用的前端项目自动化上传工具。这个工具不仅能显著提升前端部署的效率,还极大地减少了人为操作可能带来的错误。无论是中小项目,还是在网络受限的环境中,它都能发挥出出色的作用。在实际使用过程中,你可以根据自身的需求对脚本进行进一步的定制和扩展。

在最后的完整文件展示中,主要以功能实现为主,很多细节并未处理或者处理的并不妥善,比如依赖环境说明、安全性提示和代码复用性等等,所以在细节方面还有不少的提升空间,但只是个人使用的话基本没什么问题。

如果你对这个工具感兴趣,可以在 talen8/automation-deploy: 前端自动化部署 关注更新,后续我会不断去完善这个脚本功能。

评论

暂无评论,快来发表第一条评论吧!