# 通过Shell脚本以FTP方式上传文件到虚拟主机实现Hexo博客自动发布

写这篇是因为最近将博客从服务器迁移到虚拟主机上了,我需要在Linux服务器上将我的Hexo博客编译后的静态页面上传至虚拟主机上,我的虚拟主机上传文件有且仅有一种方式:FTP

先说下我想实现的自动部署流程,我在Jenkins上做了监听,一旦我的Hexo代码仓库有新的提交,会触发任务脚本执行发布操作。具体要做的事都写在Shell脚本上,主要流程如下:

  1. 脚本配置,配置Hexo工作目录,静态文件上传目录,FTP配置信息等;
  2. 进入Hexo工作目录,更新代码,清除缓存,编译生成静态页面;
  3. 递归读取Hexo静态页面目录,创建文件夹/上传文件到FTP空间;
  4. 执行Hexo deploy命令,发布静态页面,我这里配置的是发布到Github;

流程大概就是这样,Shell脚本好说,FTP工具命令是踩了不少坑。说实话这FTP工具很不友好,一些常用的功能不支持,比如不能一次创建多级目录,只能一级一级逐层创建。

那为什么不用Hexo集成的FTPSync上传插件?我试过了,怎么都上传不成功,要么卡在MKDIRs complete.上,要么卡在Committing这里,日志如下:

# 初次发布,会先创建文件夹,文件夹创建成功后就卡主了不动了
...
- /htdocs/tags/随笔 created successfuly
- /htdocs/tags/隐式转换 created successfuly
MKDIRs complete.
# 到这里卡主不动,退出重新发布一次的话,前面文件夹已经创建了,这时候卡在提交文件这里了
...
Remove:[]
Consolidation complete.
Committing
-------------------------------------------------------------
# 再次卡在这里,FTP空间里目录创建成功了,文件一个没上传上去

FTPSync插件问题有待后续跟进,其实我做成现在这套流程也是有我自己的用处的,我可以在发布脚本中加入一些自定义操作,配合Jenkins能实现很多实用的功能。


部署流程有了,那么先安装FTP工具:

yum install ftp -y

关于LinuxFTP工具命令详解文末有附上,这里先贴上我的发布脚本:

#!/bin/bash
# 博客工作目录
export BLOG_PATH=/develop/hexo
# 待上传的博客静态文件目录
export UPLOAD_PATH=/develop/hexo/public

# FTP空间访问地址
export FTP_HOST=guitu18.com
# FTP空间网站根路径
export FTP_ROOT_PATH=/htdocs
# FTP用户名
export FTP_USERNAME=guitu18.com
# FTP密码
export FTP_PASSWORD=guitu18.com

# 进入博客工作目录
cd $BLOG_PATH
echo -e "\033[44;37m>>> 博客文件更新 >>>\033[0m"
svn up
echo -e "\033[44;37m>>> 清除缓存文件 >>>\033[0m"
hexo cl
echo -e "\033[44;37m>>> 编译生成静态页面 >>>\033[0m"
hexo g

echo -e "\033[44;37m>>> 开始上传到FTP空间 >>>\033[0m"
# 递归读取目录
read_dir(){
  # 遍历文件夹
  for file in `ls -a $1`; do
    # 当前文件完整路径
    localFile=$1/$file
    # 截取掉本地路径
    len=${#UPLOAD_PATH}
    # 截取当前文件所属目标文件夹
    mdir=${1:$len}
    # 如果file存在且是一个目录
    if [ -d $localFile ]; then
      # 排除.和..目录
      if [[ $file != '.' && $file != '..' ]]; then
        # 登录FTP,在FTP空间创建对应的文件夹
        ftp -n <<EOF
open $FTP_HOST
user $FTP_USERNAME $FTP_PASSWORD
!echo '>>>mkdir: '$FTP_ROOT_PATH$mdir/$file
mkdir $FTP_ROOT_PATH$mdir/$file
close
bye
EOF
        read_dir $localFile
      fi
    else
      # 如果是文件,上传文件到FTP空间
      ftp -n <<EOF
open $FTP_HOST
user $FTP_USERNAME $FTP_PASSWORD
cd $FTP_ROOT_PATH$mdir
lcd $1
binary
hash
prompt
!echo '>>> upload: '$localFile
put $file
close
bye
EOF
      echo '>>> upload: '$localFile ' ===> 文件上传完成'
    fi
    done
}
# 执行
read_dir $UPLOAD_PATH

echo -e "\033[44;37m>>> 发布完成 >>>\033[0m"
echo -e "\033[44;37m>>> 推送至GitHub >>>\033[0m"
# 执行Hexo发布命令,发布到GitHub
hexo d
echo -e "\033[44;37m>>> 推送至GitHub完成 >>>\033[0m"

在脚本开头配置好相关参数就可以使用了,因为FTP是每次传输都需要握手,所以整个自动发布流程中最慢就是FTP上传了。

关于FTP工具的详细使用说明我找到了这篇博客:Linux文件传输FTP详解,每个命令说明讲的都比较详细了。

这里引用一些常用的命令及参数:

ftp(选项)(参数)
-d:详细显示指令执行过程,便于排错或分析程序执行的情况;
-i:关闭互动模式,不询问任何问题;
-g:关闭本地主机文件名称支持特殊字符的扩充特性;
-n:不使用自动登录;
-v:显示指令执行过程。

登录FTP实例后内部命令:

ls: 显示服务器上的目录
get: 从服务器下载指定文件到客户端
put: 从客户端传送指定文件到服务器
open: 连接ftp服务器
quit: 断开连接并退出ftp服务器
cd directory: 改变服务器的当前目录为directory
lcd directory: 改变本地的当前目录为directory
bye: 退出ftp命令状态
ascii: 设置文件传输方式为ASCII模式
binary: 设置文件传输方式为二进制模式
!: 执行本地主机命令
cd: 切换远端ftp服务器上的目录
cdup: 上一层目录
close: 在不结束ftp进程的情况下,关闭与ftp服务器的连接
delete: 删除远端ftp服务器上的文件
get: 下载
hash: 显示#表示下载进度
mdelete: 删除文件,模糊匹配
mget: 下载文件,模糊匹配
mput: 上传文件,模糊匹配
mkdir: 在远端ftp服务器上,建立文件夹
newer: 下载时,检测是不是新文件
prompt: 关闭交互模式
put: 上传
pwd: 显示当前目录

各种各样的博客系统折腾多了,终究是要返璞归真,选择将工具和内容剥离的方式,回到最初始、最纯净的 Markdown 写作需求。

Hexo,你值得拥有。