Anything0 2019-09-07
在日常开发过程中,有很多需求涉及到图片/文件上传,那么用Koa如何实现?
之前的课程讲过,Koa框架是一个基于中间件的框架,我们所需要的一些功能都需要安装相对应的中间件库。
而要实现文件上传,有很多插件:
这里推荐使用koa-body!我们来仔细研究一下它!
之前使用 koa2 的时候,处理 post 请求使用的是 koa-bodyparser,同时如果是图片上传使用的是 koa-multer。
这两者的组合没什么问题,不过 koa-multer 和 koa-route(注意不是 koa-router) 存在不兼容的问题。
koa-body结合了二者,所以koa-body可以对其进行代替。
在 koa2 中使用 koa-body,我使用的是全局引入,而不是路由级别的引入,因为考虑到很多地方都有 post 请求或者是文件上传请求,没必要只在路由级别引入。
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); }, } }));
router.post('/',async (ctx)=>{ console.log(ctx.request.files); console.log(ctx.request.body); ctx.body = JSON.stringify(ctx.request.files); });
为什么起这个标题呢,因为现在很多企业级的项目都不会选择将一些图片文件存储在自己的服务器中,为什么?
通常都会选择阿里云,腾讯云,七牛云等对象存储OSS功能。
通常每个平台都会提供自己的SDK,并配套各种示例,方便省心。不适合我们学习。
举个简单的例子
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会有多个
原理:
我们来看看upload发送的具体参数:
第一个配置(content-disposition)中的guid和第二个配置中的access_token,是我们通过webuploader配置里的formData,即传递给服务器的参数
后面几个配置是文件内容,id、name、type、size等
其中chunks为总分片数,chunk为当前第几个分片。图片中分别为12和9。当你看到chunk是11的upload请求时,代表这是最后一个upload请求了。