前言
- 【音乐博客】在做注册的时候,头像上传到服务器上,使用koa做后台
四个方面
1. 使用formidable库上传文件
var formidable = require('formidable') var form = new formidable.IncomingForm() form.uploadDir = uploadPath form.parse(ctx.req, function (err, fields, files) { console.log('files', files) //报错的时候直接抛出错误 if (err) { throw err; } //每当触发事件的时候就产生一个随机数 // var ran=parseInt(Math.random()*89999+10000); // var extname=path.extname(files.appealFile.name); // m4a 改名用的随机数 var extname = files.appealFile.name; // 要改路径,就改这两个变量的路径就好 const oldPath = __dirname + "/../" + files.appealFile.path; // console.log(oldPath); const newPath = __dirname + "/../audio/" + extname; // console.log(newPath); fs.rename(oldPath, newPath, (err) => { if (err) { console.log(err); throw Error('改名失败'); } res.send(newPath).end("success"); }) })
- 结果发现form.parse方法都进不去,更别说后面的保存操作了
- 尝试过多种方法,最后以失败告终;以前使用node是没问题的,但一到koa这就进不去,也不会报错,不明觉厉
2. 使用koa-body上传文件
- 最后换种文件上传的方法,使用流的形式上传到本地
- 安装koa-body
npm install koa-body --save
- 在koa项目中引用koa-body中间件
const koaBody = require('koa-body'); app.use(koaBody({ multipart: true, formidable: { maxFileSize: 200*1024*1024 // 设置上传文件大小最大限制,默认2M } }));
使用koa-body中间件后,即可通过ctx.request.files获取上传的文件
提醒:
新版本的koa-body通过ctx.request.files获取上传的文件
旧版本的koa-body通过ctx.request.body.files获取上传的文件获取到文件之后,通过fs将文件保存到服务器的指定目录
// 支持多文件上传到本地 const uploadFile = (fileArr) => { var resultArr = [] fileArr.forEach(element => { // 防止文件命名一致,文件名后面加上时间的时分 // const randomNum = Math.floor((Math.random() * 9 + 1)*1000); const randomNum = new Date().getHours()+""+new Date().getMinutes(); // 创建可读流 const reader = fs.createReadStream(element[1].path); let filePath = path.join(__dirname, '../public/node/upload/image/user/') + `${randomNum}_${element[1].name}`; // let filePath = path.join(__dirname, '../public/upload/image/user/') + `/${element[1].name}`; 斜杆问题注意一下 // 创建可写流 const upStream = fs.createWriteStream(filePath); // 可读流通过管道写入可写流 reader.pipe(upStream); resultArr.push({ fileName: element[1].name, filePath: filePath.replace(new RegExp(/(\\)/,"g"),'\\/'), // 上传到服务器上的真实路径 fileUrl: `/node/upload/image/user/${randomNum}_${element[1].name}`, //给前端显示头像 randomNum: randomNum }) }); return resultArr; }
3. 前端上传文件(原生)
- 写法可以做下相对应的修改,这里用的是ts编写
- html
<form id="sendAppealForm"> <a href="javascript:void(0);" class="upload">选择文件 > <span class="unfile">未选择任何文件</span> <input class="replyFileid" name="appealFile" id="appealFile" type="file" multiple="multiple" /> </a> </form> <el-button type="danger" style="width:100%; margin-bottom:30px;" @click.native.prevent="handleRegiste" >{{ $t('registe.registeIn') }}</el-button> </el-form>
private handleRegiste() { var aaa: any = document.getElementById('sendAppealForm') var formData = new FormData(aaa); var myDate = new Date(); formData.append("uploadTime", myDate.toLocaleDateString()); // 上传日期 var _this = this; this.uploadFile(formData).then(res => { console.log(res); }); } private uploadFile = (param: any) => { return new Promise((resolve, reject) => { axios .post("http://localhost:3000/users/register", param, { headers: { "Content-Type": "multipart/form-data" } }) .then(res => { resolve(res.data); }) .catch(err => { reject(err.data); }); }); };
4. 前端上传文件(饿了么组件upload)
- 手动上传文件,且只能上传单个文件,并能覆盖上传
- 先上代码,下面做分析:
<el-upload class="avatar-uploader" action="/certificateAuthentication/upload" :http-request="imgUpload" accept=".png, .jpg, .gif, .jpeg" :show-file-list="false" ref="upload" :auto-upload="false" :on-change="handleChange" :before-upload="beforeAvatarUpload" > <img v-if="imageUrl" :src="imageUrl" class="avatar" /> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> <el-button type="danger" @click.native.prevent="handleRegiste" >注册</el-button> </el-form>
fileList = []; handleChange(file, fileList) { if (fileList.length > 0) { this.fileList = [fileList[fileList.length - 1]] // 这一步,是 展示最后一次选择的csv文件 } } private handleRegiste() { (this.$refs.upload as any).submit();} private async imgUpload(e: any) { const param = new FormData(); param.append("info", JSON.stringify(this.loginForm)); param.append("file", this.fileList); // e.file const data: any = await register(param); // console.log(data); if (data.result.length !== 0) { this.$router.push({ path: "/login", query: {} }); this.$message({ message: "注册成功", type: "success" }); } }
- 因为是注册功能,我不想点击上传文件就上传到服务端去,我是要点击注册按钮才会将信息和图片一起上传上去,所以就需要手动上传
- 饿了么upload组件可以设置手动上传(自动上传关闭)
:auto-upload="false"
- 然后在注册按钮方法调用
this.$refs.upload.submit();
- 他就会去找到组件的:http-request="imgUpload"属性,然后去调用imgUpload方法
- 手动上传已解决,接下来讲一下只能上传单个文件
- file-list属性,保存的是用户选择的文件数组。
想通过on-change方法,改变file-list里选择的文件列表,只保留最后一项。
基本功能上实现了目标场景,但是有一个样式问题,因为是认为改变file-list,取最后一项,因此,用户选择第二个文件后,从第一个文件到第二个文件,有动态切换的效果,这不是我想要的,我想要的是 用户点击“上传文件”,本地电脑 选择文件,点击“确定”,页面上直接展示所选文件,不要动态切换。
css去除这部分动画
<style lang="scss" scoped> .upload-demo { display: flex; } /deep/ .el-list-enter-active, /deep/ .el-list-leave-active { transition: none; } /deep/ .el-list-enter, /deep/ .el-list-leave-active { opacity: 0; } /deep/ .el-upload-list { height: 40px; } </style>
- 至于css中的 /deep/ 是干嘛的,其实是修改elementui等第三方组件内部样式,做的渗透。如果不用scss, 可以使用 >>> 符号来修改第三方组件内部样式。
- 完美解决
最后总结
路过的猿友,要是知道koa使用formidable的用法、和为什么我进不去form.parse方法的原因,方便的话麻烦在评论区告知,谢谢哦!
参考
NodeJs koa2实现文件上传 :www.jianshu.com/p/34d0e1a5a…
vue上传文件的坑:blog.csdn.net/fengtingYan…
element ui实现手动上传文件,且只能上传单个文件,并能覆盖上传:www.cnblogs.com/lovemomo/p/…