Git打包后自动上传服务器

需求

Git全量打包和增量打包参考:Git全量打包和增量打包

打包后会在项目根目录下生成zip压缩包。上线需要将此压缩包文件上传到服务器

这里通过代码实现自动上传功能

思路

  • 在服务器端实现文件上传的功能。这里使用koa实现

  • 文件上传需要post方式提交,请求内容格式为Content-Type: multipart/form-data

  • 可以使用koa-multer实现文件上传。这里使用koa-body实现

  • 客户端发送http请求使用curl。可以到curl for windows下载安装包安装

  • 命令格式:curl -F "type=myproject/web" -F "user=sch" -F "action=upload" -F file=@"./project_202011061049.zip" http://192.168.1.10:3000/upload

  • 服务器端根据useraction参数判断是否是有效请求。有效请求的话,文件放到服务器的type参数目录下

  • 需要先递归判断目录是否存在,如果不存在则新建

  • path里面有个parse()方法,可以获取父级目录

// 在 Linux 中 path.parse('/目录1/目录2/文件.txt'); // 返回: // { // root: '/', // dir: '/目录1/目录2', // base: '文件.txt', // ext: '.txt', // name: '文件' // } // 在 Windows 中 path.parse('C:\\目录1\\目录2\\文件.txt'); // 返回: // { // root: 'C:\\', // dir: 'C:\\目录1\\目录2', // base: '文件.txt', // ext: '.txt', // name: '文件' // }

实现

服务器端实现

初始化项目

  • 新建项目目录server/

  • 打开cmd,切换到项目目录下,输入npm init -y生成package.json文件

  • 在项目目录下新建public/目录,用来存放静态文件

  • 在项目目录下新建src/目录,用来存放源代码

  • src/目录下新建server.js文件,作为服务器启动文件

使用ES6、7语法

安装babel,使用importexportasync await等语法

Babel相关知识参考:Babel7知识梳理

  • 安装:npm install @babel/core @babel/cli @babel/preset-env @babel/register @babel/node @babel/plugin-transform-runtime

  • 在根目录下新建.babelrc文件

{ "presets": ["@babel/preset-env"], "plugins": [ ["@babel/plugin-transform-runtime"] ] }
  • src/目录下新建index.js
require('@babel/register') require('./server')

自动重启

开发过程中,修改代码之后,需要重启才能看到最新的效果

这里使用nodemon来自动重启

  • 安装:npm i nodemon --save-dev

代码实现

  • 安装koa相关插件:npm i koa koa-router koa-body koa-static koa2-cors

  • 修改src/server.js文件

import path from 'path' import Koa from 'koa' import cors from 'koa2-cors' import koaStatic from 'koa-static' import koaBody from 'koa-body' import Router from 'koa-router' import apiRouter from './router' const app = new Koa() const router = new Router() // cors - 这个例子其实用不到 app.use(cors({ origin: function(ctx) { return '*' }, exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'], maxAge: 5, credentials: true, allowMethods: ['GET', 'POST', 'DELETE'], allowHeaders: ['Content-Type', 'Authorization', 'Accept'], })) // 静态文件 app.use(koaStatic(path.join(__dirname, '../public'))) // post请求数据接收。 // 配置 formidable.uploadDir 后文件会自动上传到配置的目录 // 不配置formidable.uploadDir 文件会自动上传到系统的 tmp 目录 app.use(koaBody({ multipart: true, // 支持文件上传 // formidable: { // uploadDir: path.join(__dirname, 'public/'), // keepExtensions: true, // 保存图片的扩展名 // } })) router.use('', apiRouter.routes()) app.use(router.routes()).use(router.allowedMethods()) app.listen(3000, () => { console.log('server is started at part: 3000') })
  • src/目录下新建router.js文件
import Router from 'koa-router' const router = new Router() router.post('/upload', async (ctx) => { const { type, user, action } = ctx.request.body const file = ctx.request.files && ctx.request.files.file console.log(type, user, action) console.log(file) ctx.body = { code: 0 } }) export default router
  • 我们需要先根据传过来的参数type判断对应目录是否存在,如果不存在新建目录
import fs from 'fs' import path from 'path' /** * 同步递归创建路径 * * @param {string} dir 处理的路径 * @param {function} cb 回调函数 */ const $$mkdir = function(dir, cb) { const pathinfo = path.parse(dir) if (!fs.existsSync(pathinfo.dir)) { $$mkdir(pathinfo.dir, function() { fs.mkdirSync(pathinfo.dir) }) } cb && cb() } $$mkdir(path.join(__dirname, 'demo/test/123/'))
  • 这里我们需要异步方案。在src/目录下新建utils/目录

  • src/utils/目录下新建mkdir.js文件

import fs from 'fs' import path from 'path' // 异步自动创建路径 class Mkdir { // 开始处理一个路径 async start(dir) { // 获取路径 const pathStat = await this.getStat(dir) if (pathStat) { // 路径存在:是目录返回true,不是目录返回false return pathStat.isDirectory() } // 路径不存在,需要判断上一级路径是否存在。只有上一层路径存在,才能执行新建目录操作 const pathinfo = path.parse(dir) // 递归处理上一级路径 const status = await this.start(pathinfo.dir) // 上一级路径处理失败,返回失败 if (!status) { return false } // 上一级路径有了,才能再创建当前级目录 const mkdirStatus = await this.mkdir(dir) // 返回当前级目录创建成功与否 return mkdirStatus } // 获取路径Stat getStat(loadpath) { return new Promise((resolve, reject) => { fs.stat(loadpath, (err, stats) => { if (err) { resolve(false) } else { resolve(stats) } }) }) } // 创建目录 mkdir(dir) { return new Promise((resolve, reject) => { fs.mkdir(dir, (err) => { if (err) { console.log(err) resolve(false) } else { resolve(true) } }) }) } } const mkdir = new Mkdir() export default (dir) => mkdir.start(dir)
  • 修改src/router.js文件
import path from 'path' import fs from 'fs' import Router from 'koa-router' import mkdir from './utils/mkdir' const router = new Router() router.post('/upload', async (ctx) => { const { type, user, action } = ctx.request.body const file = ctx.request.files && ctx.request.files.file if (user != 'sch' || action != 'upload') { ctx.throw(401, 'parameter error') } // 创建可读流 const reader = fs.createReadStream(file.path) // const dirPath = path.join(__dirname, '../public/', type) // 用上面的写法也可以 // 这里换一种写法:process.cwd() - 表示命令运行时所在的目录 const dirPath = path.join(process.cwd(), 'public/', type) const mkdirStatus = await mkdir(dirPath) if (!mkdirStatus) { ctx.throw(404, 'make directory error') } const uploadPath = dirPath + `/${file.name}` //创建可写流 const upStream = fs.createWriteStream(uploadPath) // 可读流通过管道写入可写流 reader.pipe(upStream) ctx.body = { code: 0, msg: 'success', data: { url: `http://${ctx.headers.host}/${type}/${file.name}` } } }) export default router

添加命令

开发环境:执行npm run server命令。使用nodemon自动重启,入口文件为src/index.js文件

线上环境:先执行npm run build命令打包,将ES6/7等语法转换成ES5,然后再执行npm run start启动项目。不需要使用nodemon自动重启,入口文件为dist/server.js

  • 修改package.json文件,添加scripts
{ // ... "scripts": { "server": "nodemon ./src/index.js", "build": "babel src --out-dir dist", "start": "node ./dist/server.js", // ... }, // ... }
  • 代码放在服务器上面启动。可以使用Nginx配置一个代理,比如代理到8080端口等。

客户端实现

  • curl for windows下载安装包安装

  • 打开cmd,输入curl --version测试是否安装成功

  • 修改post-push-full.batpost-push.bat文件,在打包完成后添加打包命令

:: ... IF "%current_branch%"=="master" ( :: ... :: http://192.168.1.10:3000/upload 是服务器端提供的上传地址 :: 如果使用代理的话可能就变成了 http://192.168.1.10:8080/upload curl -F "type=myproject/web" -F "user=sch" -F "action=upload" -F file=@"%output%" http://192.168.1.10:3000/upload pause )

创作不易,若本文对你有帮助,欢迎打赏支持作者!

 分享给好友: