(一)重点核心
(1)FileReader 及 文件流处理方案
(2)基于 Promise 管理文件异步上传
(3)文件上传的两种方案
(4)大文件切片上传
(5)基于 Node 的服务端处理
(二)知识点
文件上传有两种方案:
第一种是基于文件流(form-data),把需要上传的文件放入 form-data 中,最后将 form-data 传入服务器
第二种是基于客户端,客户端需要把文件转化 base64 编码,以 base64 的格式上传服务器
下面用 element-ui + vue + node + koa 实现一个图片服务器上传的功能
vue + element : 客户端:
客户端的代码还是很简单的
<template>
<div>
<el-upload
class="avatar-uploader"
action="/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</template>
<script>
export default {
data() {
return {
imageUrl: ''
};
},
methods: {
handleAvatarSuccess(res, file) {
// res 是接口返回的内容
// file 是上传的图片信息
// {
// "status":"success",
// "name":"timg.jpeg",
// "size":80828,
// "percentage":100,
// "uid":1605363808882,
// "raw":{"uid":1605363808882},"response":{"code":0,"message":"上传成功!"}
// }"
if (res.code === 0) {
this.imageUrl = URL.createObjectURL(file.raw)
} else {
this.$message.error(res.message)
}
},
// 上传之前先校验
beforeAvatarUpload(file) {
const isJPG = (/(png|jpeg|jpg|svg|gif)/g).test(file.type)
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!');
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return isJPG && isLt2M;
}
}
}
</script>
<style scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
node + Koa 服务端:
服务端的代码就比较复杂了。。。
我请求本地node 服务端 upload 接口
默认上传方式:mutipart/form-data,数据格式:form-data,数据包含:file:文件流信息、filename:文件名称
如果!!服务端想要解析 mutipart 这种格式的数据!必须在服务端使用 mutiparty 这个插件。。。一开始一直用 koa-bodyparser 这个插件,但是始终都获取不到。。。应该是 mutipart 这种格式无法解析,multiparty 这个插件的用法 具体看文档吧~记住服务端文件上传就要用到这个插件
const koa = require('koa')
const fs = require('fs')
const multiparty = require('multiparty')
const path = require('path')
const router = require('koa-router')
const app = new koa()
const Router = new router()
app.use(Router.routes())
Router.post('/upload', (ctx) => {
var form = new multiparty.Form({
uploadDir: __dirname + '/upload', // 指定文件存储目录
maxFilesSize: 2 * 1024 * 1024 //设置单文件大小限制
})
form.parse(ctx.req) // 将请求参数传入,multiparty会进行相应处理
form.on('field', (name, value) => { // 接收到数据参数时,触发field事件
console.log(name, value)
})
form.on('file', (name, file, ...rest) => { // 接收到文件参数时,触发file事件
// name ---> file
// file ---> {
// fieldName: 'file',
// originalFilename: '1.png',
// path: '/Users/zhoufangbing/Desktop/node/upload/QIYtZ0bHfdAFTUIA1Na_1P6s.png',
// headers: {
// 'content-disposition': 'form-data; name="file"; filename="1.png"',
// 'content-type': 'image/png'
// },
// size: 805971
// }
// 可以看出 file.path 这个参数就是图片在服务器上的地址
})
form.on('close', () => { // 表单数据解析完成,触发close事件
console.log('表单数据解析完成')
})
ctx.body = {
code: 0,
message: '上传成功!'
}
})
app.listen(3000, () => {
console.log('server is running')
})
客户端端口是 8080、服务端端口是3000
客户端直接请求服务端会跨域,所以配置了 whistle 代理
这样客户端上传完毕后:
服务端:
upload 文件夹下也有了这张图片~这就是【文件流】文件上传方式over~
--------------------------------------------------------------------------------------------------
基于客户端base64方式的上传,上一篇文章已经讲过了,使用 FileReader 这个类,道理都一样~
然后base64 的服务端会使用 Buffer,服务端生成图片
但是有一个问题:如果这张图片上传过了 还会继续上传 会重复。。。
这时候就需要一个包 SparkMd5
SparkMd5是做什么的?
SparkMd5是号称全宇宙最快的前端类包,可以无需上传文件就快速获取本地文件md5.
用这个简单的前端类库就能实现你“秒传”的功能!原理是:每个文件的md5值都是唯一的,这也是很多下载网站,会告诉你原文件的md5是多少,然后下载完毕让你自行去对比下,如果一致,就说明文件是完整的。
正因为每个文件的md5是一样的,那么,我们在做文件上传的时候,就只要在前端先获取要上传的文件md5,并把文件md5传到服务器,对比之前文件的md5,如果存在相同的md5,我们只要把文件的名字传到服务器关联之前的文件即可,并不需要再次去上传相同的文件,再去耗费存储资源、上传的时间、网络带宽。
SparkMd5 是根据文件内容来生成 hash 值的
代码就再下一篇文章中写。。使用 SparkMd5 改造一下就可以了