egg文件上传接收总结

egg获取上传文件的方法中官方给了两种处理方法,1是file直接读取,2是stream流的方式。前端

file读取方式

咱们先看看file读取的方式,该方式须要先在config里配置下ios

// config.defult.js
config.multipart = {
    mode: 'file'
  };

Control层代码axios

const  fs = require('fs')
const path = require('path')
const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
   // console.log(ctx.request.body)
     let file = ctx.request.files[0] // file包含了文件名,文件类型,大小,路径等信息,能够本身打印下看看
    
     // 读取文件
     let file = fs.readFileSync(file.filepath) //files[0]表示获取第一个文件,若前端上传多个文件则能够遍历这个数组对象
     // 将文件存到指定位置
     fs.writeFileSync(path.join('./', `uploadfile/test.png`), file)
    // ctx.cleanupRequestFiles()

   
    ctx.body = { code: 200, message: '', data: file.filename}
  }
}

前端上传文件代码(这里使用了Vue+axios)数组

<template>
  <div id="app">
    <img src="./assets/logo.png" @click="testClick">
    <input type="file" @change="upload" ref="fileid" multiple="multiple"/>
    <router-view/>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  name: 'App',
  methods: {
    testClick() {
      console.log('ddd')
    },
    upload() {
      let file = this.$refs.fileid.files[0]
      console.log(file)
      let formData = new FormData()
      formData.append('file', file)

      axios({
        method: 'post',
        url: 'http://127.0.0.1:7001/fileupload',
        data: formData
      }).then(res => {
        console.log(res)
      })
    }
  }
}
</script>

上面代码运行后能够在egg项目里的uploadfile目录里找到上传的文件缓存

stream流方式

stream流的方法,单个文件可使用getFileStream方法获取文件流 注意使用stream流方式须要把以前配置里的multipart删掉,这两种方法不能一块儿用,不然会报错。服务器

const  fs = require('fs')
const path = require('path')
const querystring =require('querystring');
const sendToWormhole = require('stream-wormhole');
const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;

    let stream = await ctx.getFileStream()
    let filename = new Date().getTime() + stream.filename  // stream对象也包含了文件名,大小等基本信息
 
    // 建立文件写入路径
    let target = path.join('./', `uploadfile/${filename}`)

    const result = await new Promise((resolve, reject) => {
      // 建立文件写入流
      const remoteFileStrem = fs.createWriteStream(target)
      // 以管道方式写入流
      stream.pipe(remoteFileStrem)

      let errFlag 
      // 监听error事件
      remoteFileStrem.on('error', err => {
        errFlag = true
        // 中止写入
        sendToWormhole(stream)
        remoteFileStrem.destroy()
        console.log(err)
        reject(err)
      })
      
      // 监听写入完成事件
      remoteFileStrem.on('finish', () => {
        if (errFlag) return
        resolve({ filename, name: stream.fields.name })
      })
    })

    ctx.body = { code: 200, message: '', data: result }
  }
}

前端上传多个文件代码app

upload() {
      let files = this.$refs.fileid.files
      let formData = new FormData()
      // 遍历文件
      for (let i = 0; i <files.length; i++) {
        let file = files[i]
        formData.append('file'+ i, file)
      }
     
      axios({
        method: 'post',
        url: 'http://127.0.0.1:7001/fileupload',
        data: formData
      }).then(res => {
        console.log(res)
      })
    }

getFileStream为上传一个文件时使用的方法,若是上传多个文件该方法只能拿到其中一个文件。多文件上传应该使用multipart方法。async

const  fs = require('fs')
const path = require('path')
const querystring =require('querystring');
const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;

    const parts = ctx.multipart();
    let part;
     while ((part = await parts()) != null) {
      if (part.length) {
        // 处理其余参数
        console.log('field: ' + part[0]);
        console.log('value: ' + part[1]);
        console.log('valueTruncated: ' + part[2]);
        console.log('fieldnameTruncated: ' + part[3]);
      } else {
        if (!part.filename) {
          
          continue;
        }
        // otherwise, it's a stream
        console.log('field: ' + part.fieldname);
        console.log('filename: ' + part.filename);
        console.log('encoding: ' + part.encoding);
        console.log('mime: ' + part.mime);
        let writePath = path.join('./', `uploadfile/${ new Date().getTime() + part.filename}`)
        let writeStrem = fs.createWriteStream(writePath)
        await part.pipe(writeStrem)     
      }
    }

    ctx.body = { code: 200, message: '', data: result }
  }
}

两种方式比较

这两种方法写下来很明显file读取的方式要简单的多,但在性能方面上这两种有什么区别呢,egg在底层是怎么实现的呢。post

使用file读取的方式咱们能够获得filepath这个路径,这个路径是用于缓存文件的地方,你们能够打印一下看看。在该路径中能够找到上传的文件。
图片描述性能

也就是说file读取方式是先在服务器里写入缓存文件,而后咱们再读取缓存文件进行操做。在上面的文件操做中file读取方式的IO操做有写入缓存文件,读取缓存文件,写入文件,总共3次IO操做。而stream流的方式没有缓存文件这个操做,也就是说IO操做只有一次,如果不将写入本服务器而是上传的OSS等则没有IO操做。那么这两种方式的效应和性能就不用多说了吧。

配置选项

file读取方式因为有文件缓存因此能够配置缓存文件的位置以及自动清除时间

config.multipart = {
  mode: 'file',
  tmpdir: path.join(os.tmpdir(), 'egg-multipart-tmp', appInfo.name), // 配置文件缓存目录
  cleanSchedule: {
   
    cron: '0 30 4 * * *',  // 自动清除时间
  },
};

也能够在代码里使用cleanupRequestFiles()方法直接清除,固然若是你选择的是stream流方式就不用管这些了。

配置文件类型和大小

config.multipart = {
   whitelist: [   // 只容许上传png格式
    '.png',
  ],
  fileSize: '5mb',  // 最大5mb  
};
相关文章
相关标签/搜索