Koa從零搭建之文件上傳

在平常開發過程當中,有不少需求涉及到圖片/文件上傳,那麼用Koa如何實現?

前言

以前的課程講過,Koa框架是一個基於中間件的框架,咱們所須要的一些功能都須要安裝相對應的中間件庫。
而要實現文件上傳,有不少插件:vue

  1. koa-body
  2. koa-bodyparser
  3. busboy
  4. koa-multer
  5. ...

這裏推薦使用koa-body!咱們來仔細研究一下它!web

koa-body

以前使用 koa2 的時候,處理 post 請求使用的是 koa-bodyparser,同時若是是圖片上傳使用的是 koa-multer。
這二者的組合沒什麼問題,不過 koa-multer 和 koa-route(注意不是 koa-router) 存在不兼容的問題。ajax

koa-body結合了兩者,因此koa-body能夠對其進行代替。npm

koa-body的基本使用

在 koa2 中使用 koa-body,我使用的是全局引入,而不是路由級別的引入,由於考慮到不少地方都有 post 請求或者是文件上傳請求,不必只在路由級別引入。安全

依賴安裝

npm i koa-body -D

app.js

const koaBody = require('koa-body');
const app = new koa();
app.use(koaBody({
  multipart:true, // 支持文件上傳
  encoding:'gzip',
  formidable:{
    uploadDir:path.join(__dirname,'public/upload/'), // 設置文件上傳目錄
    keepExtensions: true,    // 保持文件的後綴
    maxFieldsSize:2 * 1024 * 1024, // 文件上傳大小
    onFileBegin:(name,file) => { // 文件上傳前的設置
      // console.log(`name: ${name}`);
      // console.log(file);
    },
  }
}));

有用的參數

npm/koa-body服務器

獲取文件上傳後的信息

router.post('/',async (ctx)=>{
  console.log(ctx.request.files);
  console.log(ctx.request.body);
  ctx.body = JSON.stringify(ctx.request.files);
});

結果

  • size - 文件大小
  • path - 文件上傳後的目錄
  • name - 文件的原始名稱
  • type - 文件類型
  • lastModifiedDate - 上次更新時間

有錢人的選擇

爲何起這個標題呢,由於如今不少企業級的項目都不會選擇將一些圖片文件存儲在本身的服務器中,爲何?網絡

  1. 佔用空間,浪費資源
  2. 訪問速度慢
  3. 安全性低
  4. 有待補充...

一般都會選擇阿里雲,騰訊雲,七牛雲等對象存儲OSS功能。併發

一般每一個平臺都會提供本身的SDK,並配套各類示例,方便省心。不適合咱們學習。app

舉個簡單的例子框架

var OSS = require('ali-oss')

// 建立客戶端
var client = new OSS({
  region: '',
  accessKeyId: '',
  accessKeySecret: '',
  bucket: ''
})
const uploadSDK = async (obj) => {
    var fileName = obj.files.file.name
    var localFile = obj.files.file.path
    try {
      var result = await client.put(fileName, localFile)
      console.log(result.url)
    }
    catch (e) {
      console.log(e)
    }
    return result.url
}

其餘功能如圖

koa實現圖片上傳

  • fs - 文件系統
  • path - 路徑

單文件上傳

var fs = require('fs')
var path = require('path')

const uploadStatic = async (obj) => {
  // 上傳單個文件
  const file = obj.files.file
  // 建立可讀流
  const reader = fs.createReadStream(file.path);
  let filePath = path.join(__dirname, '../static/upload/') + `/${file.name}`;
  // 建立可寫流
  const upStream = fs.createWriteStream(filePath);
  // 可讀流經過管道寫入可寫流
  reader.pipe(upStream);
  return "上傳成功!";
}

多文件上傳

var fs = require('fs')
var path = require('path')

const uploadStatics = async (obj) => {
  // 上傳多個個文件
  const files = obj.files.file
  for (let file of files) {
    // 建立可讀流
    const reader = fs.createReadStream(file.path);
    let filePath = path.join(__dirname, '../static/upload/') + `/${file.name}`;
    // 建立可寫流
    const upStream = fs.createWriteStream(filePath);
    // 可讀流經過管道寫入可寫流
    reader.pipe(upStream);
  }
  return "上傳成功!";
}

擴展

大文件上傳及上傳進度獲取

涉及到大文件上傳,咱們就不能採用上面的方法,爲何?由於一般大文件上傳耗時很長,刷新/網速差等操做很容易致使文件上傳失敗,那麼如何去避免?

分片與併發結合,將一個大文件分割成多塊,併發上傳,極大地提升大文件的上傳速度。
當網絡問題致使傳輸錯誤時,只須要重傳出錯分片,而不是整個文件。另外分片傳輸可以更加實時的跟蹤上傳進度。

以vue項目爲例

基於webuploader封裝Vue組件

webuploader: 一個簡單的以H5爲主,FLASH爲輔的現代文件上傳組件
<vue-upload
        ref="uploader"
        url="xxxxxx"
        uploadButton="#filePicker"
        multiple
        @fileChange="fileChange"
        @progress="onProgress"
        @success="onSuccess"
></vue-upload>

分片的原理及流程

當咱們上傳大文件時,會被插件進行分片,ajax會有多個

  1. 多個upload請求均爲分片的請求,把大文件分紅多個小份一次一次向服務器傳遞
  2. 分片完成後,即upload完成後,須要向服務器傳遞一個merge請求,讓服務器將多個分片文件合成一個文件

原理:

  • 第一步:先對文件進行MD5的加密, 這樣有兩個好處, 便可以對文件進行惟一的標識, 爲秒傳作準備, 也能夠爲後臺進行文件完整性的校驗進行比對
  • 第二步:拿到MD5值之後, 要查詢一下, 這個文件是否已經上傳過了, 若是上傳過了, 就不用再次重複上傳, 也就是可以秒傳, 網盤裏的秒傳, 原理也是同樣的
  • 第三步:對文件進行切片, 假如文件是500M, 一個切片大小咱們定義爲50M, 那麼整個文件就爲分爲100次上傳
  • 第四步:向後臺請求一個接口, 接口裏面的數據是該文件已經上傳過的文件塊, 爲何要有這個請求呢? 咱們常常用網盤, 網盤裏面有續傳的功能, 一個文件傳到一半, 因爲各類緣由, 不想再傳了, 那麼再次上傳的時候, 服務器應該保留我以前上傳過的文件塊, 跳過這些已經上傳過的塊, 再次上傳其餘文件塊, 固然續傳方案有不少, 目前來看, 單獨發一次請求, 這樣效率最高
  • 第五步:開始對未上傳過的塊進行POST上傳
  • 第六步:當上傳成功後, 通知服務器進行文件的合併, 至此, 上傳完成!

分片

咱們來看看upload發送的具體參數:

第一個配置(content-disposition)中的guid和第二個配置中的access_token,是咱們經過webuploader配置裏的formData,即傳遞給服務器的參數 後面幾個配置是文件內容,id、name、type、size等 其中chunks爲總分片數,chunk爲當前第幾個分片。圖片中分別爲12和9。當你看到chunk是11的upload請求時,表明這是最後一個upload請求了。
相關文章
相關標籤/搜索