搭建koa基础结构
- 新建一个文件夹
- 执行npm init -y
- 安装koa koa-router
npm install koa koa-router --save
- 新建app.js引入:
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
const router = new Router()
// 路由写在最下面
app.use(router.routes())
app.use(router.allowedMethods()) // 允许http请求的所有方法
app.listen(3000, (ctx) => {
console.log('服务开启在三千端口')
})
- 开启服务:
执行node app.js,命令行打印'服务开启在三千端口'表示开启成功,因为是最简单的搭建,改了任何东西都要重新执行 node app.js
编写和引入路由
- 根目录新建route文件夹,新建index.js
- 编写index.js:
const Router = require('koa-router')
const router = new Router()
router.get('/', async (ctx, next) => {
ctx.body = ctx.request
})
module.exports = router
- app.js中引入,写在app.use(router.routes())的上面:
....
const index = require('./route/index')
/**
这里可以写成app.use('/', index.routes())
第一个参数代表这个文件路由的前缀,访问时就得加上设置的前缀
*/
router.use(index.routes())
app.use(router.routes())
app.use(router.allowedMethods()) // 允许http请求的所有方法
...
-
此时访问localhost:3000:
处理post请求参数
文件上传请求肯定是post请求,koa中处理post请求参数需要安装一个中间件
- 安装koa-body
npm install koa-body --save
- app.js中引入和使用:
...
const koaBody = require('koa-body')
// 接收post参数解析,写在路由的前面
app.use(koaBody({
multipart: true
}))
// userouter
...
我们处理文件上传需要在koaBody的配置设置multipart 为 true,这样上传的文件也就是formdata,会被koaBody处理在ctx.request.files中,其他普通的参数通过ctx.request.body就可以拿到:
拿文件:
const file = ctx.request.files['上传文件的字段名']
普通参数:
const data = ctx.request.body
编写上传接口与前端代码
- 在route文件夹下新建一个upload.js,假设规定上传的文件字段名叫file,假设只上传一个文件:
const Router = require('koa-router')
const router = new Router()
router.post('/upload', async (ctx, next) => {
const { name, path: filePath, size, type } = ctx.request.files.file
ctx.body = {
name, // 文件名称
filePath, // 临时路径
size, // 文件大小
type // 文件类型
}
})
module.exports = router
上传的文件会包含上面那个几个字段,其中path为临时路径,把他们返回,下面会把接口请求结果贴出来,看一下就知道各个字段的含义。
- app.js中引入路由与处理跨域
因为在本地模拟,前端我会直接写一个html文件,所以会出现跨域,这里顺便解决一下
npm install koa-cors --save
app中引入并使用:
// 放在最前面
const cors = require('koa-cors')
app.use(cors({
origin: '*'
}))
引入upload路由:
const upload = require('./route/upload')
router.use(upload.routes())
- 前端代码:
<body>
<input type="file" name="file" id="file" value="" />
<button id="submit">上传</button>
<script>
document.getElementById('submit').addEventListener('click', function() {
var file = document.getElementById('file').files[0]
if(!file) {
alert('请上传文件')
return
}
console.log(file)
var formdata = new FormData()
formdata.append("file", file)
var xhr = new XMLHttpRequest()
xhr.open("post", "http://localhost:3000/upload")
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
const result = JSON.parse(xhr.response)
console.log(result)
}
}
xhr.send(formdata)
})
</script>
</body>
这里用了原生的ajax,返回的response是个json字符串
- 发送请求以及返回数据
当我选择一个文件并点击上传后:
可以看到,在koa接口返回的数据这里都能正常展示,上面说到在router中,通过ctx.request.files.file拿到的path是临时路径,现在应该明确了吧,可以看到这个路径是存在我们电脑保存临时文件的地方,我们直接访问这个路径就是我们刚上传的图片,也就是说前端上传文件,在我们处理之前,koa会把我们的文件放在一个临时目录上,文件信息会放在ctx.request.files上,这时候我们只要把文件转移到我们想要的地方就可以。 - 移动文件
上面说到要把文件从临时目录移动到我们想存储的地方,移动文件可以用node原生的fs模块,这里我用一个fs的拓展库:fs-extra,操作起来比较方便,api也很简单。fs-extra文档
安装: npm install fs-extra --save
改一下我们的upload接口,upload.js:
const Router = require('koa-router')
const router = new Router()
const path = require('path')
const fse = require('fs-extra')
router.post('/upload', async (ctx, next) => {
const { name, path: filePath, size, type } = ctx.request.files.file
const dest = path.join(__dirname, '../upload', name) // 目标目录,没有没有这个文件夹会自动创建
await fse.move(filePath, dest) // 移动文件
ctx.body = {
name, // 文件名称
filePath, // 临时路径
size, // 文件大小
type // 文件类型
}
})
module.exports = router
然后我们重新上传一下文件
上传成功后,在我们koa项目的文件夹中,会自动多出来一个upload文件夹,里面就有我们上传的图片:
然后我们复制返回的filepath,也就是文件上传后的临时路径:
可以看到存在我们电脑临时目录下的文件就被移动走,相当于删除。
koa处理静态资源
其实上面已经实现了图片上传并且保存到我们想要的位置,那怎么访问呢,前端怎么展示呢,这就需要处理静态资源了。
- koa-static安装与使用
npm install koa-static --save
在app.js中:
const koaStatic = require('koa-static')
// 填上我们存放图片文件路径
app.use(koaStatic(path.join(__dirname, './upload/')))
这时候我们就可以直接通过图片的文件名访问图片了,我们上面上传的一张图片叫girl.jpg,这时候直接访问:
- 返回url与前端展示
可以看到图片可以直接展示了,所以我们这时候只需要把图片的名称返回给前端就可以,把接口修改一下,此时访问图片的url就是图片的名称,当然看你们具体的逻辑,如果上面保存图片保存在../upload/img/xxx.jpg,那么访问图片的url就变成/img/xxx.jpg
router.post('/upload', async (ctx, next) => {
const { name, path: filePath, size, type } = ctx.request.files.file
const dest = path.join(__dirname, '../upload', name) // 目标目录,没有没有这个文件夹会自动创建
await fse.move(filePath, dest) // 移动文件
ctx.body = {
name, // 文件名称
filePath, // 临时路径
size, // 文件大小
type, // 文件类型
url: name
}
})
前端加个img标签展示图片:
<body>
<input type="file" name="file" id="file" value="" />
<button id="submit">上传</button>
<img style="width:500px" src="" alt="">
<script>
document.getElementById('submit').addEventListener('click', function() {
var file = document.getElementById('file').files[0]
if(!file) {
alert('请上传文件')
return
}
console.log(file)
var formdata = new FormData()
formdata.append("file", file)
var xhr = new XMLHttpRequest()
xhr.open("post", "http://localhost:3000/upload")
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
const result = JSON.parse(xhr.response)
console.log(result)
document.querySelector('img').setAttribute('src', 'http://localhost:3000/' + result.url)
}
}
xhr.send(formdata)
})
</script>
</body>
前端上传后:
添加参数
如果需要添加其他参数,就在formdata中再append其他参数
- 前端添加参数
...
var formdata = new FormData()
formdata.append("file", file)
// 传其他参数
formdata.append("other", "test")
...
- 后端处理其他参数:
router.post('/upload', async (ctx, next) => {
const { name, path: filePath, size, type } = ctx.request.files.file
const dest = path.join(__dirname, '../upload', name)
await fse.move(filePath, dest, { overwrite: true })
ctx.body = {
name, // 文件名称
filePath, // 临时路径
size, // 文件大小
type, // 文件类型
url: name,
data: ctx.request.body // 其他参数,会自动过滤file
}
})
这时候把ctx.request.body返回给前端,看看是什么样的:
可以看到 ctx.request.body 会自动过滤掉上传的文件参数,通过这个我们就可以拿到其他普通的参数,这个逻辑和一些框架中的上传组件是一样的。
到这里整个功能就实现了,欢迎大家指教哦。