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  
};
相關文章
相關標籤/搜索