在平常開發過程當中,有不少需求涉及到圖片/文件上傳,那麼用Koa如何實現?vue
以前的課程講過,Koa框架是一個基於中間件的框架,咱們所須要的一些功能都須要安裝相對應的中間件庫。 而要實現文件上傳,有不少插件:web
這裏推薦使用koa-body!咱們來仔細研究一下它!ajax
以前使用 koa2 的時候,處理 post 請求使用的是 koa-bodyparser,同時若是是圖片上傳使用的是 koa-multer。 這二者的組合沒什麼問題,不過 koa-multer 和 koa-route(注意不是 koa-router) 存在不兼容的問題。npm
koa-body結合了兩者,因此koa-body能夠對其進行代替。安全
在 koa2 中使用 koa-body,我使用的是全局引入,而不是路由級別的引入,由於考慮到不少地方都有 post 請求或者是文件上傳請求,不必只在路由級別引入。bash
npm i koa-body -D
複製代碼
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);
});
複製代碼
爲何起這個標題呢,由於如今不少企業級的項目都不會選擇將一些圖片文件存儲在本身的服務器中,爲何?網絡
一般都會選擇阿里雲,騰訊雲,七牛雲等對象存儲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
}
複製代碼
其餘功能如圖
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: 一個簡單的以H5爲主,FLASH爲輔的現代文件上傳組件
<vue-upload
ref="uploader"
url="xxxxxx"
uploadButton="#filePicker"
multiple
@fileChange="fileChange"
@progress="onProgress"
@success="onSuccess"
></vue-upload>
複製代碼
當咱們上傳大文件時,會被插件進行分片,ajax會有多個
原理:
第一步:先對文件進行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請求了。