在實作上傳接口的時候,需要了解一下上傳的過程跟需要依賴的庫,以及測試上傳需要注意的問題。這裡順便說一下,注冊的時候去掉預設的圖檔頭像了,還有預設的簽名。
圖檔上傳到伺服器的邏輯
- 首先前端調用上傳接口,将上傳的資源經過 FormData 執行個體封裝之後,傳給服務端
- 在服務端接收前端傳進來的圖檔資訊,通過
方法,來讀取圖檔内容,并存放在變量中fs.readFileSync
- 找個存放圖檔的公共位置
- 通過
方法,将圖檔内容寫入公共位置fs.writeFileSync
- 最後傳回圖檔位址
依賴的庫
需要對:
fs
、
moment
、
path
、
mkdirp
,有一定了解,fs,path 大家應該比較熟悉了,這裡就對 moment 以及 mkdirp 做一下簡單的介紹,具體的可以檢視文檔:
- mkdirp
- moment
mkdirp
mkdirp 是一款在 node.js 中像
mkdir -p
一樣遞歸建立目錄及其子目錄。
安裝
npm install
使用
var mkdirp = require('mkdirp');
mkdirp('/tmp/foo/bar/baz', function (err) {
if (err) console.error(err)
else console.log('kaimo')
});
// 輸出 kaimo,/tmp/foo/bar/baz 目錄就會出現。
moment
Moment.js 是一個輕量級的 JavaScript 時間庫,它友善了日常開發中對時間的操作,提高了開發效率。
安裝
npm install
使用
var moment = require('moment');
moment().format();
// 格式化日期時間
moment(value).format('YYYY-MM-DD HH:mm:ss');
// moment(Date):可以使用預先存在的原生 Javascript Date 對象來建立 Moment。
var day = new Date(2011, 9, 16);
var dayWrapper = moment(day);
// 這會克隆 Date 對象,Date 的後續更改不會影響 Moment,反之亦然。
eggjs 裡檔案上傳接收
egg 擷取上傳檔案的方法中官方給了兩種處理方法:這裡采用用 file 直接讀取
- file 直接讀取
- stream 流的方式:建立檔案寫入流,以管道方式寫入流
file 讀取方式
需要先在
config.defult.js
裡配置:
config.multipart = {
mode:'file'
};
控制層代碼:
// file 包含了檔案名,檔案類型,大小,路徑等資訊
// files[0]表示擷取第一個檔案,若前端上傳多個檔案則可以周遊這個數組對象
let file = ctx.request.files[0]
// 讀取檔案
let filedata = fs.readFileSync(file.filepath);
// 将檔案存到指定位置
fs.writeFileSync(path.join('./', `uploadfile/test.png`), filedata)
關于 stream 流的方式可以參考:【egg檔案上傳接收總結】
上傳接口實作
0、配置
需要先在
config.defult.js
裡配置:檔案讀取配置,以及上傳頭像路徑,我們單獨把頭像放到本地的
kaimo-cost-images
檔案夾裡,跟項目分開,就不放到app的public檔案夾裡了,這個不需要手動去 D 盤建立檔案夾,到時會自動生成的。
// add your user config here
const userConfig = {
// myAppName: 'egg',
uploadAvatarDir: 'D://kaimo-cost-images/images/avatar', // 上傳頭像路徑
};
// 檔案讀取配置
config.multipart = {
mode: 'file'
};
1、建立路由
配置完,開始去
router.js
檔案裡寫一下請求上傳的路由
// 上傳頭像
router.post('/api/upload/avatar', verify_token, controller.upload.uploadAvatar);
2、建立 upload.js 檔案
在 controller 檔案夾下建立
upload.js
用于處理上傳的相關邏輯
3、編寫上傳邏輯
- 擷取檔案,
表示擷取第一個檔案,若前端上傳多個檔案則可以周遊這個數組對象ctx.request.files[0]
- 擷取目前日期
- 建立圖檔儲存的路徑
- 建立目錄
- 生成路徑傳回
- 寫入檔案夾
- 清除臨時檔案,
;ctx.cleanupRequestFiles()
'use strict';
const fs = require('fs');
const path = require('path');
const moment = require('moment');
const mkdirp = require('mkdirp');
const Controller = require('egg').Controller;
class UploadController extends Controller {
async uploadAvatar () {
const { ctx, config } = this;
try {
// 0、擷取檔案
let file = ctx.request.files[0];
console.log('擷取檔案', file);
// ctx.request.files[0] 表示擷取第一個檔案,若前端上傳多個檔案則可以周遊這個數組對象
let fileData = fs.readFileSync(file.filepath);
console.log('fileData', fileData);
// 1、擷取目前日期
let day = moment(new Date()).format('YYYYMMDD');
console.log('1、擷取目前日期', day);
// 2、建立圖檔儲存的路徑
let dir = path.join(config.uploadAvatarDir, day);
console.log('2、建立圖檔儲存的路徑', dir);
// 3、建立目錄
await mkdirp(dir);
// 4、生成路徑傳回
let date = Date.now(); // 毫秒數
let tempDir = path.join(dir, date + path.extname(file.filename)); // 傳回圖檔儲存的路徑
console.log('毫秒數 extname', date, path.extname(file.filename));
console.log('傳回圖檔儲存的路徑', tempDir);
// 5、寫入檔案夾
fs.writeFileSync(tempDir, fileData);
ctx.body = {
status: 200,
desc: '上傳成功',
data: tempDir,
}
} catch(error) {
ctx.body = {
status: 500,
desc: '上傳失敗',
data: null
}
} finally {
// 6、清除臨時檔案
ctx.cleanupRequestFiles();
}
}
}
module.exports = UploadController;
測試接口
還是老樣子,先登入,然後拿到 token,在上傳一張圖檔,比如我選擇一隻修勾勾,然後點選發送。
我們可以看到成功傳回了圖檔所在的路徑。當然實際項目裡不會傳回伺服器的路徑的,下一節我們在優化處理。