本文用于自我总结以及给予新手一些借鉴借此共勉
demo地址 => 传送门
ps:觉得有帮助的同学点个赞或者github star 在此谢过
使用版本:
-
koa: 2.6.1
-
express: 4.X
-
webpack: 4.19.1
-
mongoose: 5.3.9
实现的功能:
批量上传和下载
mongoose数据库操作
增删查
webpack4部分优化配置
jwt登录控制、路由鉴权
单文件下载&批量下载:
tips:批量下载需要使用引入archiver第三方包将多个文件打包成一个压缩文件
- Koa:
//此处分为文件和图片两个接口 router.get('/downloadFile/:name',//文件 async ctx => { ctx.set('Content-disposition', `attachment;filename=${ctx.params.name}`); const paths = `src/assets/upload/files/${ctx.params.name}` const data = fs.createReadStream(paths) // 发送路径文件内容的文件 ctx.body = data } ).get('/downloadImg/:name',//图片 async ctx => { ctx.set('Content-disposition', `attachment;filename=${ctx.params.name}`); const paths = `src/assets/upload/imgs/${ctx.params.name}`; ctx.attachment(paths); await send(ctx, paths); } ) //批量下载 router.get('/batchDownload', async ctx => { const list = [{ name: '1.jpg', path: 'src/assets/upload/imgs/1.jpg' }, { name: '1.csv', path: 'src/assets/upload/files/1.csv' }, { name: '2.png', path: 'src/assets/upload/imgs/2.png' }];//name为压缩文件内生成的文件名称,path为要下载的文件路径 const zipName = 'download.zip';//自定义生成的压缩文件名称 const zipPath = `src/assets/download/${zipName}`;//自定义生成的压缩文件路径 const zipStream = fs.createWriteStream(zipPath); const zip = archiver('zip'); zip.pipe(zipStream); for (let i = 0; i < list.length; i++) { // 添加单个文件到压缩包 zip.append(fs.createReadStream(list[i].path), { name: list[i].name }) } await zip.finalize(); ctx.attachment(zipPath); await send(ctx, zipPath); } )
- Express:
//此处同Koa app.get('/downloadFile/:name', (req, res) => { const filePath = `src/assets/upload/files/${req.params.name}` if (fs.existsSync(filePath)) { res.download(filePath) } else { res.json({ err: 'file is not exist', success: false }) } } ).get('/downloadImg/:name', (req, res) => { const filePath = `src/assets/upload/imgs/${req.params.name}` if (fs.existsSync(filePath)) { res.download(filePath) } else { res.json({ err: 'file is not exist', success: false }) } } ).get('/batchDownload', async (req, res) => { const list = [{ name:'1.jpg',path: 'src/assets/upload/imgs/1.jpg' }, { name:'1.csv',path: 'src/assets/upload/files/1.csv' }, { name:'2.png',path: 'src/assets/upload/imgs/2.png' }]; const zipName = 'download.zip'; const zipPath = `src/assets/download/${zipName}`; const zipStream = fs.createWriteStream(zipPath); const zip = archiver('zip'); zip.pipe(zipStream); for (let i = 0; i < list.length; i++) { // 添加单个文件到压缩包 zip.append(fs.createReadStream(list[i].path), { name: list[i].name }) } await zip.finalize(); setTimeout(() => { res.download(zipPath) }, 0); } )
上传&批量上传:
- Koa:
//接收post请求需要先处理options请求 app.use( async (ctx, next) => { if (ctx.request.method === "OPTIONS") { ctx.response.status = 200 ctx.set('Access-Control-Allow-Origin', ctx.request.headers.origin) ctx.set("Access-Control-Max-Age", 24 * 60 * 60 * 1000); ctx.set("Access-Control-Allow-Methods", 'GET,POST,OPTIONS,DELETE,PUT'); ctx.set("Access-Control-Allow-Headers", 'Origin,Content-Type,Authorization,Accept,X-Custom-Header,anonymous,X-Requested-With') ctx.body = '' } else { await next() } } ) //上传 router.post('/upload', ctx => { try { const files = ctx.request.files.file files.length ? files.forEach(file => fs .createReadStream(file.path) .pipe(fs.createWriteStream(`${file.type.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${file.name}`)) ) : fs .createReadStream(files.path) .pipe(fs.createWriteStream(`${files.type.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${files.name}`)) ctx.body = '上传成功' } catch (e) { ctx.body = '上传失败' } } )
- Express(需要引入multer接受upload文件):
const multer = require('multer')const upload = multer({ dest: '/src/upload' })
//处理options app.all('*', (req, res, next) => { if (req.method === "OPTIONS") { res.status(200) res.end() } else { next() } } ).post('/upload', upload.any(), (req, res, next) => { try { const files = req.files console.log(files) files.length ? files.forEach(file => fs .createReadStream(file.path) .pipe(fs.createWriteStream(`${file.mimetype.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${file.originalname}`)) ) : fs .createReadStream(files.path) .pipe(fs.createWriteStream(`${files.mimetype.indexOf('image') >= 0 ? 'src/assets/upload/imgs' : 'src/assets/upload/files'}/${files.originalname}`)) res.json({ err: 'upload success', success: false }) } catch (e) { res.json({ err: 'upload failed', success: false }) } } )
mongodb线上数据mLab库:
分为两个文件 connect.js连接数据库,db.js导出mongodb的自定义数据模型
- connect.js
const mongoose = require('mongoose') mongoose.Promise = global.Promise mongoose.connect('mongodb://用户名:密码@ds145093.mlab.com:45093/用户名', { useNewUrlParser: true }, (err, info) => { if (err) throw 'mongodb connect failed' console.log('mongodb connect success') } )
- db.js
require('./connect') const mongoose = require('mongoose'); const Schema = mongoose.Schema; const todoSchema = new Schema({ name: String, isDoing: Boolean, createDate: Date, }, {versionKey: false}) const Todo = mongoose.model('Todo', todoSchema) exports.Todo=Todo;
增删查:
- Koa:
//需要引入koa-body const koaBody = require('koa-body') //查询所有记录 app.get('/todoList', async ctx => { try { let data = await Todo.find({}) ctx.body = { data, success: true } } catch (e) { ctx.body = { data: [], success: false } } }, ).get('/todoList/:name',//模糊查询单一记录 async ctx => { const params = { name: { $regex: new RegExp(`${ctx.params.name}`) } } try { let data = await Todo.find(params) ctx.body = { data, success: true } } catch (e) { ctx.body = { data: [], success: false } } } ).post('/todoList', koaBody(),//新增记录 async ctx => { const todo = new Todo({ ...ctx.request.body }) try { let data = await todo.save() ctx.body = { data, success: true } } catch (err) { ctx.body = { data: { err }, success: false } } } ).del('/todoList/:name',//删除记录 async ctx => { const params = { name: ctx.params.name } try { let data = await Todo.findOneAndDelete(params) ctx.body = { data, success: true } } catch (err) { ctx.body = { data: { err }, success: false } } } )
- Express:
//查询所有记录 app.get('/todoList', async (req, res, next) => { try { let data = await Todo.find({}) res.json({ data, success: true }) } catch (e) { res.json({ data: [], success: false }) } }, ).get('/todoList/:name',//模糊查询记录 async (req, res, next) => { const params = { name: { $regex: new RegExp(`${req.params.name}`) } } try { let data = await Todo.find(params) res.json({ data, success: true }) } catch (e) { res.json({ data: [], success: false }) } } ).post('/todoList',//新增记录 async (req, res, next) => { const todo = new Todo({ ...req.body }) try { let data = await todo.save() res.json({ data, success: true }) } catch (err) { res.json({ data: { err }, success: false }) } } ).delete('/todoList/:name',//删除记录 async (req, res, next) => { const params = { name: req.params.name } try { let data = await Todo.findOneAndDelete(params) res.json({ data, success: true }) } catch (err) { res.json({ data: { err }, success: false }) } } )
webpack优化配置:
- 打包第三方库预编译:
//dll.js const path = require('path') const webpack = require('webpack') module.exports = { entry: { vendor: [ 'react', 'react-dom', 'react-router-dom', 'immutable', 'antd', 'axios', ], }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, '../public'), library: '[name]_library', }, plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }), new webpack.DllPlugin({ path: path.resolve(__dirname, '../public','[name]-manifest.json'), name: '[name]_library', context: __dirname }) ], }
- 提取公共代码
optimization: { splitChunks: { chunks: 'all', name: false, cacheGroups: { commons: { name: 'commons', priority: 10, chunks: 'initial' } }, } }
- happypack
//引入happypack const HappyPack = require('happypack') new HappyPack({ id: 'jsx', threads: 4, loaders: ['babel-loader?presets[]=react,presets[]=latest&compact=false'], })
- 藉由路由分包
//引入react-loadable import Loadable from 'react-loadable' const MyLoadingComponent = ({ isLoading, error }) => { return '' } const Nav = Loadable({ loader: () => import(/* webpackChunkName: "Nav" */'../components/Nav'),//注释是为了让webpack打包出来的文件以路由为名 loading: MyLoadingComponent })
- 以上的优化足够一般的项目使用,还有其他一些零散的优化配置如减少编译后文件大小:Tree-shaking等,缓存loader: cache-loader,文件hash缓存:webpack-md5-plugin等 有兴趣的可以自行Google或百度