在平常开发过程当中,有不少需求涉及到图片/文件上传,那么用Koa如何实现?vue
以前的课程讲过,Koa框架是一个基于中间件的框架,咱们所须要的一些功能都须要安装相对应的中间件库。 而要实现文件上传,有不少插件:web
这里推荐使用koa-body!咱们来仔细研究一下它!ajax
以前使用 koa2 的时候,处理 post 请求使用的是 koa-bodyparser,同时若是是图片上传使用的是 koa-multer。 这二者的组合没什么问题,不过 koa-multer 和 koa-route(注意不是 koa-router) 存在不兼容的问题。npm
koa-body结合了两者,因此koa-body能够对其进行代替。安全
在 koa2 中使用 koa-body,我使用的是全局引入,而不是路由级别的引入,由于考虑到不少地方都有 post 请求或者是文件上传请求,不必只在路由级别引入。bash
npm i koa-body -D
复制代码
const koaBody = require('koa-body');
const app = new koa();
app.use(koaBody({
multipart:true, // 支持文件上传
encoding:'gzip',
formidable:{
uploadDir:path.join(__dirname,'public/upload/'), // 设置文件上传目录
keepExtensions: true, // 保持文件的后缀
maxFieldsSize:2 * 1024 * 1024, // 文件上传大小
onFileBegin:(name,file) => { // 文件上传前的设置
// console.log(`name: ${name}`);
// console.log(file);
},
}
}));
复制代码
npm/koa-body服务器
router.post('/',async (ctx)=>{
console.log(ctx.request.files);
console.log(ctx.request.body);
ctx.body = JSON.stringify(ctx.request.files);
});
复制代码
为何起这个标题呢,由于如今不少企业级的项目都不会选择将一些图片文件存储在本身的服务器中,为何?网络
一般都会选择阿里云,腾讯云,七牛云等对象存储OSS功能。并发
一般每一个平台都会提供本身的SDK,并配套各类示例,方便省心。不适合咱们学习。app
举个简单的例子
var OSS = require('ali-oss')
// 建立客户端
var client = new OSS({
region: '',
accessKeyId: '',
accessKeySecret: '',
bucket: ''
})
const uploadSDK = async (obj) => {
var fileName = obj.files.file.name
var localFile = obj.files.file.path
try {
var result = await client.put(fileName, localFile)
console.log(result.url)
}
catch (e) {
console.log(e)
}
return result.url
}
复制代码
其余功能如图
var fs = require('fs')
var path = require('path')
const uploadStatic = async (obj) => {
// 上传单个文件
const file = obj.files.file
// 建立可读流
const reader = fs.createReadStream(file.path);
let filePath = path.join(__dirname, '../static/upload/') + `/${file.name}`;
// 建立可写流
const upStream = fs.createWriteStream(filePath);
// 可读流经过管道写入可写流
reader.pipe(upStream);
return "上传成功!";
}
复制代码
var fs = require('fs')
var path = require('path')
const uploadStatics = async (obj) => {
// 上传多个个文件
const files = obj.files.file
for (let file of files) {
// 建立可读流
const reader = fs.createReadStream(file.path);
let filePath = path.join(__dirname, '../static/upload/') + `/${file.name}`;
// 建立可写流
const upStream = fs.createWriteStream(filePath);
// 可读流经过管道写入可写流
reader.pipe(upStream);
}
return "上传成功!";
}
复制代码
涉及到大文件上传,咱们就不能采用上面的方法,为何?由于一般大文件上传耗时很长,刷新/网速差等操做很容易致使文件上传失败,那么如何去避免?
分片与并发结合,将一个大文件分割成多块,并发上传,极大地提升大文件的上传速度。 当网络问题致使传输错误时,只须要重传出错分片,而不是整个文件。另外分片传输可以更加实时的跟踪上传进度。
以vue项目为例
webuploader: 一个简单的以H5为主,FLASH为辅的现代文件上传组件
<vue-upload
ref="uploader"
url="xxxxxx"
uploadButton="#filePicker"
multiple
@fileChange="fileChange"
@progress="onProgress"
@success="onSuccess"
></vue-upload>
复制代码
当咱们上传大文件时,会被插件进行分片,ajax会有多个
原理:
第一步:先对文件进行MD5的加密, 这样有两个好处, 便可以对文件进行惟一的标识, 为秒传作准备, 也能够为后台进行文件完整性的校验进行比对
第二步:拿到MD5值之后, 要查询一下, 这个文件是否已经上传过了, 若是上传过了, 就不用再次重复上传, 也就是可以秒传, 网盘里的秒传, 原理也是同样的
第三步:对文件进行切片, 假如文件是500M, 一个切片大小咱们定义为50M, 那么整个文件就为分为100次上传
第四步:向后台请求一个接口, 接口里面的数据是该文件已经上传过的文件块, 为何要有这个请求呢? 咱们常常用网盘, 网盘里面有续传的功能, 一个文件传到一半, 因为各类缘由, 不想再传了, 那么再次上传的时候, 服务器应该保留我以前上传过的文件块, 跳过这些已经上传过的块, 再次上传其余文件块, 固然续传方案有不少, 目前来看, 单独发一次请求, 这样效率最高
第五步:开始对未上传过的块进行POST上传
第六步:当上传成功后, 通知服务器进行文件的合并, 至此, 上传完成!
咱们来看看upload发送的具体参数:
第一个配置(content-disposition)中的guid和第二个配置中的access_token,是咱们经过webuploader配置里的formData,即传递给服务器的参数 后面几个配置是文件内容,id、name、type、size等 其中chunks为总分片数,chunk为当前第几个分片。图片中分别为12和9。当你看到chunk是11的upload请求时,表明这是最后一个upload请求了。