koa 图片上传详解
本文用图片上传展示koa中间件框架基本流程,省去前端编码直接用
httpie
模拟文件上传测试
Koa
Koa 的中间件之间按照编码顺序在栈内依次执行(先进后出),允许您执行操作并向下传递请求(downstream),之后过滤并逆序返回响应(upstream)。前端人员可以将next()
之前的任意代码视为“捕获”阶段,下面这个 gif 说明了 async 函数如何恰当地利用堆栈流来实现请求和响应流。
当中间件(带有签名 (ctx, next) 的MiddlewareFunction)运行时,它必须手动调用 next() 来运行 “下游” 中间件。
Code
图片上传,需要指定上传路径(步骤5),web访问展示图片则要开启静态资源服务(步骤1)
const logger = require("koa-logger")
const serve = require("koa-static")
const koaBody = require("koa-body")
const Koa = require('koa')
const fs = require("fs")
const app = new Koa()
const os = require("os")
const path = require("path")
app.use(logger())
// 使用文件上传中间件
app.use(koaBody({ multipart: true }))
// 1.静态资源服务,指定对外提供访问的根目录
app.use(serve(path.join(__dirname, '/public')));
app.use(async function (ctx, next) {
await next()
if (ctx.body || !ctx.idempotent) return
ctx.response.body = '<h1>Hello, koa2!</h1><p>not upload photo</p>'
})
// 2. 文件上传
app.use(async function (ctx, next) {
if ('POST' != ctx.method) return await next()
// 获取图片源
// <input type="file" name="file" multiple>
const file = ctx.request.files.file
// 接收读出流
const reader = fs.createReadStream(file.path)
// 创建写入流
// 3. 指定图片路径文件名(即上传图片存储目录)
const stream = fs.createWriteStream(path.join('public/images', file.name))
// 用管道将读出流 "倒给" 输入流
reader.pipe(stream)
// 4.打印上传文件在服器上存储的相对路径
console.log('uploading %s -> %s', file.name, stream.path)
// 5.重定向到基于根目录下的静态资源web访问路径,展示图片
ctx.redirect(stream.path.substr(6).replace(/\\/g,'/'))
})
// 监听
app.listen(3000, () => {
console.log("listening port 3000");
})
Upload
项目基本结构
└─node_modules
├─public
└─images
└─app.js
执行node app.js
启动服务器
listening port 3000
执行httpie 命令上传本地图片到服务器
服务端显示
<-- POST /
uploading pipe.png -> public\images\pipe.png
--> POST / 302 73ms 63b
查看服务器图片目录,与此同时前端可以通过localhost:3000/images/pipe.png
url地址直接访问图片
Busboy
你也可用busboy模块来实现文件上传
// busboy 模块是用来解析POST请求,node原生req中的文件流
const Koa = require('koa')
const app = new Koa()
const inspect = require('util').inspect
const path = require('path')
const fs = require('fs')
const Busboy = require("busboy")
function upload(ctx) {
const busboy = new Busboy({ headers: ctx.req.headers })
// 监听文件解析事件
busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
console.log(`File [${fieldname}]: filename: ${filename}`)
// 将文件保存到特定路径
file.pipe(fs.createWriteStream(path.join('public/images/',filename)))
// 开始解析文件流
file.on('data', function (data) {
console.log(`File [${fieldname}] got ${data.length} bytes`)
})
// 解析文件结束
file.on('end', function () {
console.log(`File [${fieldname}] Finishied`);
})
})
// 监听请求中的字段
busboy.on('field', function (fieldname, val, fieldnameTruncated, valTruncated) {
console.log(`Field [${fieldname}]: value: ${inspect(val)}`);
})
// 监听结束事件
busboy.on('finish', function () {
console.log('Done parsing form!');
})
// 后续处理
ctx.req.pipe(busboy)
}
app.use(async (ctx) => {
if (ctx.url === '/' && ctx.method === 'POST') {
console.log('上传了文件!')
upload(ctx)
} else {
console.log('请用httpie上传文件')
}
})
app.listen(3000, () => {
console.log('[demo] request post is starting at port 3000')
})
用httpie上传表单传字段及文件
C:\Users\Administrator>http -f post :3000/ pic@C:\Users\Administrator\Pictures\stl\033.jpg age=34
HTTP/1.1 404 Not Found
Connection: keep-alive
Content-Length: 9
Content-Type: text/plain; charset=utf-8
Date: Mon, 30 Dec 2019 04:51:06 GMT
控制台打印
上传了文件!
Field [age]: value: '34'
File [pic]: filename: 033.jpg
File [pic] got 65084 bytes
File [pic] got 65536 bytes
File [pic] got 11545 bytes
File [pic] Finishied
Done parsing form!
本作品采用《CC 协议》,转载必须注明作者和本文链接