一文搞懂eggjs中上传文件

9,821 阅读2分钟

一、基本的配置

eggjs中上传文件的官方文档

  • 1、配置上传的路由

    module.exports = (app) => {
      const { router, controller } = app;
      router.resources('file', '/api/v1/file', controller.file);
    };
    
  • 2、在config/config.default.js中配置上传的基本配置(可以参考官方文档)

    ...
    // 配置上传
    config.multipart = {
      fileSize: '50mb',
      mode: 'stream',
      fileExtensions: ['.xls', '.txt'], // 扩展几种上传的文件格式
    };
    ...
    
  • 3、安装几个基本的包

    npm install await-stream-ready stream-wormhole dayjs
    

二、使用form表单上传

  • 1、模板的代码

    <form method="POST" action="/api/v1/file" enctype="multipart/form-data">
      title: <input name="title" /> file: <input name="file" type="file" />
      <button type="submit">Upload</button>
    </form>
    
  • 2、在控制器中使用fs及数据流写入文件(在控制器层)
async create() {
  // 获取文件流
  const stream = await this.ctx.getFileStream();
  // 定义文件名
  const filename = Date.now() + path.extname(stream.filename).toLocaleLowerCase();
  // 目标文件
  const target = path.join('app/public/uploads', filename);
  //
  const writeStream = fs.createWriteStream(target);
  console.log('-----------获取表单中其它数据 start--------------');
  console.log(stream.fields);
  console.log('-----------获取表单中其它数据 end--------------');
  try {
    //异步把文件流 写入
    await awaitWriteStream(stream.pipe(writeStream));
  } catch (err) {
    //如果出现错误,关闭管道
    await sendToWormhole(stream);
    // 自定义方法
    this.error(err);
  }
  // 自定义方法
  this.success({ url: '/public/uploads/' + filename });
}
  • 3、可以将生成的url地址传递服务层,存到数据库中

三、使用ajax上传文件

  • 1、基本的html页面

    <h1>ajax提交</h1>
    <input type="text" id="username" />
    <input name="file" type="file" id="file" />
    <button id="btn">提交</button>
    
  • 2、jqajax

    <script>
      $('#btn').on('click', function() {
        console.log('提交按钮');
        let formData = new FormData();
        formData.append('title', $('#username').val());
        formData.append('image', $('#file')[0].files[0]);
        console.log(formData);
        $.ajax({
          url: '/api/v1/file',
          data: formData,
          method: 'post',
          contentType: false,
          processData: false,
          success: function(result) {
            console.log(result);
          },
        });
      });
    </script>
    

四、对上传的文件根据时间分类归档

  • 1、修改上传代码的控制器

    const fs = require('fs');
    const path = require('path');
    //故名思意 异步二进制 写入流
    const awaitWriteStream = require('await-stream-ready').write;
    //管道读入一个虫洞。
    const sendToWormhole = require('stream-wormhole');
    const dayjs = require('dayjs');
    
    async create() {
      const stream = await this.ctx.getFileStream();
      console.log('-----------获取数据 start--------------');
      console.log(stream.fields);
      console.log('-----------获取数据 end--------------');
      // 基础的目录
      const uplaodBasePath = 'app/public/uploads';
      // 生成文件名
      const filename = `${Date.now()}${Number.parseInt(
        Math.random() * 1000,
      )}${path.extname(stream.filename).toLocaleLowerCase()}`;
      // 生成文件夹
      const dirname = dayjs(Date.now()).format('YYYY/MM/DD');
      function mkdirsSync(dirname) {
        if (fs.existsSync(dirname)) {
          return true;
        } else {
          if (mkdirsSync(path.dirname(dirname))) {
            fs.mkdirSync(dirname);
            return true;
          }
        }
      }
      mkdirsSync(path.join(uplaodBasePath, dirname));
      // 生成写入路径
      const target = path.join(uplaodBasePath, dirname, filename);
      // 写入流
      const writeStream = fs.createWriteStream(target);
      try {
        //异步把文件流 写入
        await awaitWriteStream(stream.pipe(writeStream));
      } catch (err) {
        //如果出现错误,关闭管道
        await sendToWormhole(stream);
        this.error();
      }
      this.success({ url: path.join('/public/uploads', dirname, filename) });
    }
    
  • 2、其它的都不变

  • 3、上传生成的目录

    2019
    └── 06
        └── 21
            ├── 1561097630832563.png
            └── 1561097675536863.jpeg
    

五、提取到基类的控制器中并且进行传递参数分类

  • 1、在基类的控制器中

    const { Controller } = require('egg');
    const fs = require('fs');
    const path = require('path');
    //故名思意 异步二进制 写入流
    const awaitWriteStream = require('await-stream-ready').write;
    //管道读入一个虫洞。
    const sendToWormhole = require('stream-wormhole');
    const dayjs = require('dayjs');
    
    class BaseController extends Controller {
      // 上传文件的通用方法
      async uploadFile(category = '') {
        const stream = await this.ctx.getFileStream();
        // 基础的目录
        const uplaodBasePath = 'app/public/uploads';
        // 生成文件名
        const filename = `${Date.now()}${Number.parseInt(
          Math.random() * 1000,
        )}${path.extname(stream.filename).toLocaleLowerCase()}`;
        // 生成文件夹
        const dirname = dayjs(Date.now()).format('YYYY/MM/DD');
        function mkdirsSync(dirname) {
          if (fs.existsSync(dirname)) {
            return true;
          } else {
            if (mkdirsSync(path.dirname(dirname))) {
              fs.mkdirSync(dirname);
              return true;
            }
          }
        }
        mkdirsSync(path.join(uplaodBasePath, category, dirname));
        // 生成写入路径
        const target = path.join(uplaodBasePath, category, dirname, filename);
        // 写入流
        const writeStream = fs.createWriteStream(target);
        try {
          //异步把文件流 写入
          await awaitWriteStream(stream.pipe(writeStream));
        } catch (err) {
          //如果出现错误,关闭管道
          await sendToWormhole(stream);
          return {
            error: '错误',
          };
        }
        return {
          url: path.join('/public/uploads', category, dirname, filename),
          fields: stream.fields,
        };
      }
    }
    module.exports = BaseController;
    
  • 2、在上传文件的控制器中使用

    const Controller = require('./../core/base_controller');
    class FileController extends Controller {
      async create() {
        // 上传头像的,会在uploads文件夹下有个avatar的文件夹下面才是2019、06、21
        const { url, fields } = await this.uploadFile('avatar');
        const result = await 服务层;
        this.success(url);
      }
    }
    
  • 3、其它的都不变