本篇博客适合了解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中就可以打印出这些信息了:
在这里插入图片描述
并且将音频文件存入到指定的目录中
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐