vue2 + egg.js使用FormData传递表单和文件(上传音频)
Posted structrue
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue2 + egg.js使用FormData传递表单和文件(上传音频)相关的知识,希望对你有一定的参考价值。
本篇博客适合了解vue项目、了解eggjs、了解axios的小伙伴阅读,如果不是熟悉这些技术,可以先去看看相关视频或者网站学习。
一份表单中包含有普通参数、也有要上传的文件,将这些数据放到formdata中传递到后端进行处理。
我这里的前端用的是vue2,页面仅有一个表单,页面的代码如下:
<template>
<div class="about">
<el-form :model="form"
status-icon
label-width="100px"
class="demo-ruleForm">
<el-form-item label="内容"
prop="content">
<el-input type="text"
v-model="form.content"
autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="文件"
prop="audio">
<el-upload class="avatar-uploader"
action=""
:show-file-list="false"
:before-upload="beforeUpload"
:on-change="change">
上传
</el-upload>
<audio :src="audioSrc"
controls="controls" />
<div>filename</div>
</el-form-item>
<el-form-item>
<el-button type="primary"
@click="submitForm">提交</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import $api from "../utils/api";
export default
data: () =>
return
filename: "",
form: content: "" ,
fileBase64: "",
audioSrc: "",
formdata: new FormData(),
;
,
watch: ,
mounted() ,
methods:
change(file)
console.log(file);
const isMp3 = file.raw.type === "audio/mpeg";
if (!isMp3)
return 0;
else
//这的file并不是我们真正要传递的file,file.raw才是
this.formdata.append("upfile", file.raw); //将file.raw放到formdata中
this.formdata.append("filename", file.raw.name); //将文件名也放到formdata中
this.filename = file.raw.name;
this.base64_encode(file.raw); //调用base64_encode函数
,
//传入file,将文件转为blob类型
base64_encode(file)
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () =>
// console.log("file 转 base64结果:" + reader.result); //reader.result是base64码,但是因为音频较长的时候,需要转为blob才能播放
this.fileBase64 = reader.result;
let audioBlob = this.base64ToBlob(this.fileBase64, "mp3"); //将base64转为Blob
this.audioSrc = window.URL.createObjectURL(audioBlob); //设置页面上的音频的src
;
,
//将base64转为blob
base64ToBlob(base64, type)
let arr = base64.split(",");
let array = arr[0].match(/:(.*?);/); //arr[0]是base64的开头说明
let mime = (array && array.length > 1 ? array[1] : type) || type;
let bytes = window.atob(arr[1]); //arr[1]是完整的base64码 window.atob()返回一个解码后的字符串
// console.log(bytes);
let abuffer = new ArrayBuffer(bytes.length); //二进制的数据缓冲区
// console.log(Object.prototype.toString.call(abuffer));
let u8a = new Uint8Array(abuffer);
for (let i = 0; i < bytes.length; i++)
u8a[i] = bytes.charCodeAt(i);
return new Blob([abuffer],
type: mime,
); //最后返回一个Blob数据
,
beforeUpload(file)
const isMp3 = file.type === "audio/mpeg";
if (!isMp3)
this.$message.error("上传头像图片只能是 mp3 格式!");
return false;
,
submitForm()
this.formdata.append("content", this.form.content); //模拟的表单普通文本
$api
.post("/audio", this.formdata) //请求后egg端接口/audio并把formdata传过去
.then((res) =>
console.log(res); //得到结果
if (res.data.status == 1)
//存入成功
this.$message(
message: "提交成功",
duration: 1000,
type: "success",
);
this.formdata = new FormData(); //重新设置formdata
else
this.$message(
message: "提交失败",
duration: 1000,
type: "error",
);
)
.catch((e) =>
console.log(e);
);
,
,
;
</script>
前端使用的是axios来发起请求,因为我们使用FormData传递文件数据,所以需要设置请求头中的content-type为 “multipart/form-data”
import axios from "axios";
const $api = axios.create( baseURL: "http://127.0.0.1:7001" )
$api.interceptors.request.use(function (config)
config.headers['content-type'] = "multipart/form-data";
return config;
, function (error)
return Promise.reject(error);
);
export default $api;
egg后端代码,先在config.default.js中设置multipart,这里的设置官网有,但是官网只写了mode:“file”,我使用的时候,系统会报错。
这里设置一下文件大小,我设置到100mb大,可以上传小于100mb的文件。
文件类型我只做了MP3的例子,根据需要修改。
config.multipart =
// mode: "file",
fileSize: '100mb',
mode: 'stream',
fileExtensions: ['.mp3'], // 扩展几种上传的文件格式
;
router.js文件中设置接口路由
'use strict';
/**
* @param Egg.Application app - egg application
*/
module.exports = app =>
const router, controller = app;
router.post('/audio', controller.home.uploadAudio);
;
控制器中写入uploadAudio方法,egg需要安装await-stream-ready、stream-wormhole、formidable插件
'use strict';
const Controller = require('egg').Controller;
const fs = require('fs');
const path = require('path');
const awaitWriteStream = require('await-stream-ready').write;
const sendToWormhole = require('stream-wormhole');
const audioBaseUrl = 'app/public/audio';//这是我的音频存放位置
const formidable = require("formidable");
class HomeController extends Controller
async uploadAudio()
const ctx = this;
try
function parse(req) //使用formidable解析formdata
const form = new formidable.IncomingForm();
return new Promise((resolve, reject) =>
form.parse(req, (err, fields, files) =>
resolve( fields, files );
reject((res) => console.log(res) )
);
);
const extraParams = await parse(this.ctx.req);
console.log("FormData中的普通参数-----------", extraParams.fields);
console.log("FormData中的文件--------------", extraParams.files);
const file = extraParams.files.upfile; //得到文件upfile,我的vue传过来的file就叫做upfile,所以这里取出来也是通upfile
const stream = fs.createReadStream(file._writeStream.path);//创建文件读取流,从临时文件中读取
const fileName = extraParams.fields.filename;//文件命名
//为保证文件的命名不重复,结合自己的项目数据库的需要,取时间戳加用户id或者其他方式
const target = path.join(audioBaseUrl, fileName);//目标路径
const writeStream = fs.createWriteStream(target);//将问价写入目标路径
try
await awaitWriteStream(stream.pipe(writeStream));//等待完成写入
catch (err)
await sendToWormhole(stream);//关闭临时文件
console.log(err)
ctx.body = status: 1
catch (err)
console.log(err)
ctx.body = status: 0
module.exports = HomeController;
页面显示如下:
点击“上传”,即可选择文件,选好MP3文件后,页面可以进行播放。
egg中就可以打印出这些信息了:
并且将音频文件存入到指定的目录中
Egg.js 学习笔记01
Controller概念:解析用户输入,处理并返回响应的结果;
框架中,Controller层主要是对用户的请求的参数进行处理(校验、转换),然后调用 service 方法处理业务:
- 获取用户通过HTTP传递过来的请求参数
- 校验,组装参数
- 调用Service进行业务处理,必要时转换Service返回的结果以适应客户端需求
- 通过HTTP将结果响应给客户端
一个简单的Controller如下:(返回body内容而已)
const Controller = require(\'egg\') class IndexController extends Controller async index() const ctx = this ctx.body = \'hi, eggJs\' module.exports = IndexController
一个稍微复杂一点的Controller 如下 目录位置:app/controller/post.js
const Controller = require(\'egg\'); class PostController extends Controller async create() const ctx, service = this; const createRule = title: type: \'string\' , content: type: \'string\' , ; // 校验参数 ctx.validate(createRule); // 组装参数 const author = ctx.session.userId; const req = Object.assign(ctx.request.body, author ); // 调用 Service 进行业务处理 const res = await service.post.create(req); ctx.body = id: res.id ; ctx.status = 201; module.exports = PostController;
注意上面这个demo是不能直接运行的,ctx下的属性和方法我们并没有创建,怎么解决呢?
因为Controller继承自egg.Controller,会有几个属性挂在this上分别是:
this.ctx, this.app, this.service, this.config, this.logger
其中 this.ctx,即请求的上下文Context,在这里就可以封装我们的属性和方法,也就是上面demo的解决方式
目录位置:app/extend/context.js
module.exports = validate(param) // 校验参数的方法 console.log(\'参数是:\', param); , ;
Query参数
Controller是业务开发中唯一和http协议打交道的地方,所以难免遇到参数获取的问题,这里获取方式和Koa类似:
在URL中?后面的部分是一个 Query String,在get请求中传递参数。如:/post?id=12&username=green,我们可以通过ctx.query拿到解析过后的参数
URL: http://127.0.0.1:7002/post?id=12&name=green
class PostController extends Controller async listPost() const query = this.ctx.query //id:"12",name:"green"
注意:当客户端传递的key有重复的情况下,ctx.query只取第一次出现的值,后面重复key的值均忽略
如果设计成客户端传递相同的key,如 http://127.0.0.1:7002/post?id=12&name=green&id=13&name=king,可以使用ctx.queries获取参数,参数将以数组的形式作为值存在对应的键下
id:["12","13"],name:["green","king"]
Params参数
在路由上,如果申明的参数,可以通过ctx.params获取
URL: http://127.0.0.1:7002/188/post?id=12&name=green&id=13&name=king 路由 router.get(\'/:testId/post\', controller.post.create); class postController extends Controller async listApp() console.log(this.ctx.params.testId,); // testId: "188"
以上是关于vue2 + egg.js使用FormData传递表单和文件(上传音频)的主要内容,如果未能解决你的问题,请参考以下文章