Egg实现上传文件到阿里云OSS

570 阅读3分钟

Egg实现上传文件到阿里云OSS

用到的包

  • egg-multipart【egg中默认使用】

  • ali-oss

  • crypto

  • path

File模式

Egg配置

 exports.multipart = {
    mode: 'file',
    allowArrayField: true,
    fileSize: '5mb',
   	// 如果报错 Invalid filename ,egg-multipart有对上传文件的后缀名限制的白名单(whitelist),.md后缀没有在默认whitelist中,导致上传失败。
    fileExtensions: ['.md'],
}

代码

'use strict'

const Controller = require('egg').Controller
const path = require('path')
const OSS = require('ali-oss')
const fs = require('mz/fs')
const crypto = require('crypto')

class UploadController extends Controller {
  // file 模式
  async upload() {
    const { ctx } = this
    const file = ctx.request.files[0]
		
    // 请查看阿里云OSS使用文档
    const client = new OSS({
      region: 'oss-cn-beijing',
      bucket: 'yxh-test-bucket',
      accessKeyId: '----',
      accessKeySecret: '----',
    })

    // 获取后缀
    const extname = path.extname(file.filename).toLocaleLowerCase()
    // 获取源文件的文件名(不包含后缀名)
    const originFileName = path.basename(file.filename, extname)

    // 生成唯一的文件名
    const md5 = crypto.createHash('md5')
    const timestamp = new Date().getTime() // 当前时间戳
    const randomNum = Math.ceil(Math.random() * 1000) // 取1000以下的随机数
    const filename =
      md5
        .update(path.basename(file.filename, extname) + timestamp + randomNum)
        .digest('hex') + extname

    try {
      // 处理文件,上传到OSS
      const result = await client.put(
        '(' + originFileName + ')' + filename,
        file.filepath
      )
      ctx.body = {
        code: 200,
        result,
      }
    } catch (e) {
      // 需要删除临时文件
      await fs.unlink(file.filepath)
      ctx.body = {
        code: 110,
        msg: e,
      }
    }
  }
}

module.exports = UploadController

Stream 模式

如果你对于 Node 中的 Stream 模式非常熟悉,那么你可以选择此模式。在 Controller 中,我们可以通过 ctx.getFileStream() 接口能获取到上传的文件流。

Egg配置

 exports.multipart = {
    mode: 'file',
    allowArrayField: true,
    fileSize: '5mb',
   	// 如果报错 Invalid filename ,egg-multipart有对上传文件的后缀名限制的白名单(whitelist),.md后缀没有在默认whitelist中,导致上传失败。
    fileExtensions: ['.md'],
}

代码

'use strict'

const Controller = require('egg').Controller
const path = require('path')
const OSS = require('ali-oss')
const fs = require('mz/fs')
const crypto = require('crypto')
const sendToWormhole = require('stream-wormhole')

class UploadController extends Controller {
  // stream 模式,需要在 config.default.js 中修改 multipart.mode = 'stream'
  async uploadStream() {
    const { ctx } = this
    const stream = await ctx.getFileStream()

    // 获取参数
    console.log(stream)

    if (!stream.filename) {
      ctx.body = {
        code: 110,
        msg: '上传失败,请重新尝试',
      }
      return
    }

    const client = new OSS({
      region: 'oss-cn-beijing',
      bucket: 'yxh-test-bucket',
      accessKeyId: '---',
      accessKeySecret: '---',
    })

    // 获取后缀
    const extname = path.extname(stream.filename).toLocaleLowerCase()
    // 获取源文件的文件名(不包含后缀名)
    const originFileName = path.basename(stream.filename, extname)

    // 生成唯一的文件名
    const md5 = crypto.createHash('md5')
    const timestamp = new Date().getTime() // 当前时间戳
    const randomNum = Math.ceil(Math.random() * 1000) // 取1000以下的随机数
    const filename =
      md5
        .update(path.basename(stream.filename, extname) + timestamp + randomNum)
        .digest('hex') + extname

    try {
      const result = await client.putStream(
        '(' + originFileName + ')' + filename,
        stream
      )
      ctx.body = {
        code: 200,
        result,
      }
    } catch (err) {
      console.log(err)
      await sendToWormhole(stream)
      ctx.body = {
        code: 110,
        msg: '上传失败',
      }
    }
  }
}

module.exports = UploadController

更详细的使用方式请参考官网。

踩坑(报错记录)

1、filetype undefined (图片可以上传,不报错)

**原因:**没有给egg指定上传文件的类型

解决方法:config中添加如下配置

// config/config.default.js
config.multipart = {
  mode: 'file',  // 对应文件类型 
}

2、Invalid filename

在上传markdown文件或者txt或者apk的时候会出现

原因:egg-multipart有对上传文件的后缀名限制的白名单(whitelist),.txt后缀没有在默认whitelist中,导致上传失败。

解决方法:config中添加如下配置

// config/config.default.js
config.multipart = {
  fileExtensions: [
    '.txt',
  ],
}

后缀名限制的白名单: www.eggjs.org/zh-CN/basic…

3、Reach fileSize limit

**原因:**超出文件限制的大小

解决方法:config中添加如下配置

// config/config.default.js
config.multipart = {
  fileSize: '100mb',  // Max file size (in bytes), default is 10mb
}

参考

1、阿里云OSS使用文档

help.aliyun.com/document_de….

2、Egg官方-获取上传的文件

www.eggjs.org/zh-CN/basic…