需求
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
-
服务器端根据
user
和action
参数判断是否是有效请求。有效请求的话,文件放到服务器的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
,使用import
、export
、async 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.bat
和post-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
)
发表评论