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

Egg.js 为企业级框架和应用而生

Controller概念:解析用户输入,处理并返回响应的结果;

框架中,Controller层主要是对用户的请求的参数进行处理(校验、转换),然后调用 service 方法处理业务:

  1. 获取用户通过HTTP传递过来的请求参数
  2. 校验,组装参数
  3. 调用Service进行业务处理,必要时转换Service返回的结果以适应客户端需求
  4. 通过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传递表单和文件(上传音频)的主要内容,如果未能解决你的问题,请参考以下文章

egg.js之中间件

egg.js之中间件

egg.js整合socket.io

使用egg.js和egg-sequelize连接mysql

egg.js 中使用 egg-mysql 操作 mysql 数据库

HTML5可预览多图片ajax上传(使用formData传递数据)