這篇文章給大家分享的是個人用自建圖牀搭建過程。
圖牀,也就是專門提供存儲圖片的地方,我們只要通過圖牀提供的 API 接口,把圖片上傳上去,就可以通過外鏈訪問了,我們在CSDN發表文章,上傳圖片,其實就是用的CSDN的圖牀,但CSDN的圖牀有時候也挺不方便的。
比如,在我剛開始寫博客的時候,我喜歡先在本地寫,博客中的圖片我都存儲在本地的文件夾裏。
然後在本地寫完了,保存爲.md文件。
再打開CSDN、上傳Markdown,然後就會出現這樣的錯誤:
如果一篇文章的圖片多起來,事情就麻煩了,有時候還會漏掉一些圖片。
如果我們自己搭建圖牀,直接把這些圖片存儲在雲端,圖片的來源是http服務,就沒有這些問題了,本文就是以該思路來提供一個解決方案。
最終效果如下:
爲了實現這樣的效果,你需要:
- 一點Node.js的知識
- 一臺VPS服務器,Linux、Windows都可以(本文使用的是Win Server、坑比Linux要多得多)
- 本文僅用作Mac用戶作參考,因爲用到了upic(開源圖牀客戶端)
下載並且安裝uPic
uPic(upload Picture) 是一款 Mac 端的圖牀(文件)上傳客戶端,可以將圖片、各種文件上傳到配置好的指定提供商的對象存儲中。
然後快速獲取可供互聯網訪問的文件 URL
不要去APP Store下載。
點擊這個鏈接,前往Github下載dmg文件。
或者使用HomeBrew安裝
brew cask install upic
在VPS服務器上安裝minio
minio是可以部署在本地的對象存儲服務。
根據VPS的系統不同,在官方文檔裏都能夠找到對應的安裝方法。
Mac和Linux安裝都很方便,Windows下載下來就是一個exe,都不帶安裝過程的。
在CMD裏啓動服務:
./minio.exe server ./data
其中./data是指定的對象存儲的文件所在位置。
打開之後默認會偵聽9000端口,訪問該端口可以進入服務。
然後,Win比較頭疼的是如何將這句話變成一個服務,畢竟你總不能一直掛個命令行在上面吧。
先創建一個minio.bat文件,然後寫入如下內容
@echo off
set MINIO_ACCESS_KEY=access_key
set MINIO_SECRET_KEY=secret_key
C:\Users\Administrator\Desktop\個人網站\minio.exe server C:\Users\Administrator\Desktop\個人網站\文件共享
將第四行的兩個絕對路徑分別替換成minio的路徑和對象存儲的路徑即可。
然後可以運行一下./minio.bat看看是否能夠成功運行。
要注意MINIO_ACCESS_KEY和MINIO_SECRET_KEY這兩個參數,相當於minio的用戶名和密碼。
然後我們將這個腳本註冊成服務,使用cmd的sc命令,格式如下:
sc create minioServer binpath=C:\Users\Administrator\Desktop\個人網站\minio.bat start=auto
同樣,把腳本的絕對路徑替換成自己的。
這樣我們就註冊完服務,在服務管理頁面能夠看到該服務了,可以設置爲開機自啓動。
編寫Nodejs後端
代碼很簡單,使用的Expressjs,抄的學長的代碼。
const express = require('express')
const multer = require('multer')
const Minio = require('minio')
const path = require('path')
const fs = require('fs')
const upload = multer({ dest: './tmp/' })
upload.limits = {
fileSize: 10 * 1024 * 1024 // 最大 10MB
}
const app = express()
// minio 客戶端
const minioClient = new Minio.Client({
endPoint: 'xxxxxx', // 替換 minio 的訪問地址
port: 9000,
useSSL: false,
accessKey: 'xxxxx', // 替換 accessKey
secretKey: 'xxxxxx' // 替換 secretKey
});
const handleError = (err, res) => {
res
.status(500)
.contentType("text/plain")
.end("Oops! Something went wrong!");
};
app.post('/', upload.single('file'), function(req, res, next) {
// 檢查 token
const token = req.get('token')
if (token !== 'xxxxxxx') { // 替換鑑權 token
return handleError('', res)
}
// 文件臨時存儲文件夾
const {originalname, path: tmpPath} = req.file
// 文件後綴
const extname = path.extname(originalname).toLowerCase()
if (['.png', '.jpg', '.jpeg', '.gif'].indexOf(extname) !== -1) {
// minio 中保存的文件名
const filePath = `${tmpPath}.${extname}`
// 獲取月份
const today = new Date()
const month = today.getMonth() + 1
const year = today.getFullYear()
// 重命名文件
fs.rename(tmpPath, filePath, err => {
if (err) return handleError(err, res)
// 上傳指 minio 中
minioClient.fPutObject("resource", `img/${year}/${month}/${originalname}`, filePath, {
'Content-Type': `image/${extname.split('.')[1]}`,
}, async () => {
await fs.unlink(filePath, function() {})
res.json({
"data": `http://ip/resource/img/${year}/${month}/${originalname}` // 替換 ip
})
})
});
} else {
// 刪除文件
fs.unlink(tmpPath, err => {
if (err) return handleError(err, res);
})
res.status(403).contentType("text/plain").end("Only img files are allowed!");
}
})
app.listen(3002, () => {
console.log("app listening on port 3002")
})
然後用pm2部署一下就ok了。
我們偵聽的是3002端口,實際上有域名的話可以做個轉發,這裏我就不多講了。
uPic客戶端配置
點擊其他字段新增一個Header
:
注意:這裏的token字段是在nodejs代碼中設置的。
然後驗證一下:
並且能夠支持截圖、剪貼板、文件上傳,很完美!