📜 Midway 使用 MinIo

阿里云 FC 环境,文件需要小于 6M。

演示 Demoopen in new window

先使用 Midway 创建一个项目

npm init midway
1

选择

⊙ Serverless Application Migration
  egg-layer-midway - A egg-layer boilerplate for Midway v2 application migration
1
2

然后安装所需要的依赖

npm i minio  stream-wormhole
1

开启 文件上传

// src/config/config.default.ts
config.multipart = {
  mode: "stream", // file | stream | buffer
};

config.multipart = {
  // 表单 Field 文件名长度限制
  fieldNameSize: 100,
  // 表单 Field 内容大小
  fieldSize: "100kb",
  // 表单 Field 最大个数
  fields: 10,

  // 单个文件大小
  fileSize: "10mb", // 如果要上传大文件,就需要修改这里
  // 允许上传的最大文件数
  files: 10,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这里直接使用 steam 模式,其他两种模式看 参考 中的 Egg 文献。

如果是部署在 阿里云 Serverless ,上传大文件你会遇见:

{
  "ErrorCode": "EntityTooLarge",
  "ErrorMessage": "payload size exceeds maximum allowed size (6291456 bytes)"
}
1
2
3
4

Controller

import {
  App,
  Controller,
  Get,
  Inject,
  Post,
  Provide,
} from "@midwayjs/decorator";
import { Application, Context } from "egg";
import { FileStream } from "../../typings/app";
import * as sendToWormhole from "stream-wormhole";
import { MinIoService } from "../service/minio";

@Provide()
@Controller("/")
export class HomeController {
  @App()
  app: Application;

  @Inject()
  ctx: Context;

  @Inject()
  service: MinIoService;

  @Get("/")
  async home() {
    const html = `
    <form action="/upload" method="POST" enctype="multipart/form-data">
      bucket : <input name="bucket" /> <br>
      file name : <input name="fileNanme" /> <br>
      <input type="file" name="file"> <br>
      <button type="submit">上传</button>
    </form>
    `;
    return html;
  }

  @Post("/upload")
  async upload() {
    const stream: FileStream = await this.ctx.getFileStream();
    try {
      const { bucket, fileNanme } = stream?.fields;
      // console.log('body > ',stream.fields);
      // console.log('file > ',stream.filename)

      const res = await this.service.saveFile({
        bucketName: bucket || "midway",
        objectName: `test/${fileNanme || stream.filename}`,
        stream: stream as any,
      });

      return { flag: true, code: 200, msg: "上传成功", info: res };
    } catch (error) {
      // 必须将上传的文件流消费掉,要不然浏览器响应会卡死
      await sendToWormhole(stream);

      return { flag: false, code: 500, msg: error.message };
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

通过 await this.ctx.getFileStream(); 获取流,是一个 FileStream(egg 提供) ,有文件的详细信息。

FileStream 的 fields 还提供 表单其他字段。

不得不说,Egg 是真的强大。

Service

import { Provide } from "@midwayjs/decorator";
import * as minio from "minio";
import { Readable } from "stream";

@Provide()
export class MinIoService {
  private config = {
    endPoint: "www.you.domain.com",
    port: 443, // http 80 , https 443
    useSSL: true,
    accessKey: "x x x x x x x", // 用户ID
    secretKey: "yyyyy/zzzzzzzzzz", // pwd
  };

  private minio: minio.Client;

  constructor() {
    this.minio = new minio.Client(this.config);
  }

  async saveFile(q: {
    bucketName: string;
    objectName: string;
    stream: Readable | Buffer | string;
    size?: number;
    contentType?: any;
  }) {
    const { bucketName, objectName, stream, size, contentType } = q;

    return new Promise((ok, err) => {
      this.minio.putObject(
        bucketName,
        objectName,
        stream,
        size,
        contentType,
        (e, tag) => {
          if (e) return err(e);

          ok(tag);
        }
      );
    });
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

参考