共计 5368 个字符,预计需要花费 14 分钟才能阅读完成。
简述
本文主要是记录在搭建博客过程中,随着数据规模不断增长,存储和管理变得越来越重要。所以当时考虑使用图床或者 OSS 服务,于是买了一年的私人图床,因为价格便宜经常掉线,后面考虑更换 OSS 对象存储服务 。但是没钱,所以我考虑自己在服务区上部署一套 minio 对象存储服务 。下面是对 minio 的介绍以及这次部署的流程。
什么是 MinIO ?
根据官网的表述简单整理,MinIO 是一个开源的、轻量级、易于部署的对象存储服务器。它采用分布式架构,可以在多个节点上进行数据存储和访问,实现数据的冗余备份和高可用性。同时,它还具备强大的扩展性,可以根据需要进行水平扩展,以满足不断增长的存储需求。它可以用于构建私有云存储、备份存储、数据湖等各种场景。
环境准备
下载最新的稳定的 MinIO 二进制文件并将其安装到系统。
wget https://dl.min.io/server/minio/release/linux-amd64/minio | |
chmod +x minio | |
sudo mv minio /usr/local/bin/ |
启动服务
// 新建 monio 数据文件夹 | |
mkdir -p /admin/minio/data /admin/minio/config | |
minio server /admin/minio/data --console-address :9999 |
启动成功后得到如下 log,证明 MinIO 启动成功。

在浏览器中输入公网 ip + 端口 9999 能看到如下界面。
配置 systemctl
MinIO 已经部署完成,接下来使用 systemctl 来管理 MinIO。
使用 systemctl 管理 MinIO 有以下好处:
- 简化管理:systemctl 命令轻松地启动、停止、重启和重新加载 MinIO 服务,而无需手动操作
- 自动启动:通过将 MinIO 服务配置为自动启动,您可以确保在系统启动时自动启动 MinIO,无需手动干预。
- 状态监控:systemctl 提供了检查 MinIO 服务状态的命令,您可以随时查看服务是否正在运行以及其健康状态。
- 日志管理:systemctl 还提供了查看 MinIO 服务日志的命令,帮助您诊断和解决问题。
创建 systemd 服务单元文件
// 创建 minio.service 文件 | |
touch /etc/systemd/system/minio.service | |
// 配置内容 | |
vi /etc/systemd/system/minio.service | |
[ | ]|
Description=MinIO Server // MinIO 服务的名称 | |
Documentation=https://min.io/docs/minio/linux/index.html // 这个无所谓可以随便写,我写了 minio 文档的地址 | |
After=network.target // 在网络连接完成后启动 MinIO 服务 | |
[ | ]|
ExecStart=/root/minio server /admin/minio/data --console-address :9999 // 指定启动 MinIO 服务的命令行 /root/minio ==> minio 的安装位置 /admin/minio/data ==> minio 服务储存数据的目录 --console-address :9999 ==> 设置 minio 控制台的地址和端口 | |
Restart=always // 如果服务意外停止,自动重新启动 | |
Environment="MINIO_ROOT_USER=xxxxxx" "MINIO_ROOT_PASSWORD=xxxxxx" // 设置环境变量,用于指定 MinIO 的根用户和密码 | |
[ | ]|
WantedBy=multi-user.target // 指定在多用户模式下启动 MinIO 服务 |
启动 minio 服务
systemctl start minio | |
systemctl status minio |
得到如下结果怎么 minio 服务启动完成
新建 minio 访问密钥和储存桶
使用上面配置的账户密码进入 minio 管理界面,在下方 user 界面新建一个用户
新增完成后点进去,在这边创建密钥
在新建密钥界面就可以看到两个 key,一定要保存下来,这玩意只会出现一次
完成密钥创建后再去 buckets 界面创建储存桶
以上步骤完成后就可以在 nestjs 中使用 minio 了
nestjs 使用 minio 上传图片
安装官方提供的 minio javascript 包
npm install minio -S
全局配置文件加入 minio 配置 app.config.ts
export const minioConfig = { | |
endPoint: 'xxx.xxx.xxx.xxx', // 服务器公网 ip | |
port: 9000, // minio 挂载的端口,没指定的话一般是 9000 | |
useSSL: false, // 是否开启 SSL | |
accessKey: 'xxxxxxxxxxxxxxx', // 访问密钥 | |
secretKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 密钥 | |
}; |
minio.service.ts 编写上传文件逻辑
import {Injectable} from '@nestjs/common'; | |
import {HttpService} from '@nestjs/axios'; | |
import * as Minio from 'minio'; | |
import {v4} from 'uuid'; | |
import * as dayjs from 'dayjs'; | |
() | |
export class MinIOService { | |
private readonly minioClient: Minio.Client; | |
constructor(private readonly httpService: HttpService) {this.minioClient = new Minio.Client(minioConfig); | |
} | |
// 多图片上传 | |
async uploadImageMultiple(file: Array<Express.Multer.File>) {let promiseList = []; | |
const length = file.length; // 文件数量 | |
promiseList = file.map((item) => {return this.uploadImageWithMinio(item.originalname, item.buffer); | |
}); | |
return Promise.all(promiseList).then((res) => {const status = res.map((item) => item.status); // 上传状态 | |
const trueCount = status.filter((item) => item === true).length; // 成功统计 | |
const falseCount = status.filter((item) => item === false).length; // 失败统计 | |
const links = []; | |
const error = []; | |
res.forEach((item) => {if (item.status) {links.push(item.data.links.url); | |
} else {error.push(item); | |
} | |
}); | |
if (error.length > 0) {console.log('上传图片 Error', error); | |
} | |
const returnData = {msg: ` 共上传 ${length} 张图片,成功 ${trueCount},失败 ${falseCount}`, | |
data: links, | |
}; | |
return returnData; | |
}); | |
} | |
// 使用 minio 上传图片 | |
async uploadImageWithMinio(objectName: string, data: Buffer) { | |
try {const fileExtension = objectName.match(/\.([^.]+)$/)[1]; // 获取文件后缀 | |
const newfilename = `${v4()}-${dayjs().format('YYYYMMDDHHmmss')}.${fileExtension}`; // 使用 uuid + 当前时间重写文件名 | |
await this.minioClient.putObject('blogpic', newfilename, data); // 调用 minio 提供的方法上传对象 | |
return { | |
status: true, | |
data: { | |
links: {url: `https://dawdlepig.cn/blogpic/${newfilename}`, // 返回文件地址,这里我使用 nginx 做了代理,后面再说,一般文件地址是 ip + port+ bucketName + fileName | |
}, | |
}, | |
}; | |
} catch (error) {console.log('oss 图片上传失败', error); | |
return { | |
status: false, | |
error | |
}; | |
} | |
} | |
} |
monio.controller.ts 组装接口
import {Controller, HttpException, HttpStatus, Post, UploadedFiles, UseInterceptors} from '@nestjs/common' | |
import {AnyFilesInterceptor} from '@nestjs/platform-express'; | |
import {RequestResult} from 'src/common/interface/common.interface'; | |
import {MinIOService} from './minio.service' | |
'minio') | (|
export class MinIOController {constructor(private readonly minioService: MinIOService) {} | |
'uploadImageOSS') | (|
AnyFilesInterceptor()) | (|
async uploadImageOSS( () files: Array<Express.Multer.File>, | |
): Promise<RequestResult> {if (!files || files.length === 0) {throw new HttpException('file 不能为空!', HttpStatus.OK); | |
} | |
const imageType = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; | |
files.forEach((file) => {if (!imageType.includes(file.mimetype)) {throw new HttpException('文件类型错误', HttpStatus.OK); | |
} | |
}); | |
try {const res = await this.systemService.uploadImageMultiple(files, 'oss'); | |
return { | |
data: res.data, | |
msg: res.msg, | |
}; | |
} catch (error) {console.error(error); | |
throw new HttpException({ error, message: '图片上传失败'}, | |
HttpStatus.OK, | |
); | |
} | |
} | |
} |
自己按照 nest 文档写一下 module 就行
nginx 代理 minio
一般咱们发布网站都是有域名的,很少裸奔,所以我们的图片地址也需要使用 nginx 代理一下,下面是我的配置
location /blogpic/ { | |
proxy_set_header X-Forwarded-Proto $scheme; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_set_header Host $http_host; | |
proxy_connect_timeout 300; | |
proxy_http_version 1.1; | |
proxy_set_header Connection ""; | |
chunked_transfer_encoding off; | |
proxy_pass http://localhost:9000; | |
} |
可能遇到的错误
NoSuchkey ==> 检查自己的文件名是否正确
AccessDenied ==> 检查 bucket 权限,是否设置成 Private 了,可以在 Buckets 界面更改,设置成 Public 或者自定义权限
你的工作,由 AI 赋能!🔥
还在为文案、脚本卡壳、做视频、写代码、设计图片灵感枯竭而烦恼吗?🤯
板板 AI,你的工作好帮手!
一键生成 各种文案、脚本、图片、视频、代码、报告,轻松应对各种工作 / 营销需求!
现在注册体验,即可获得:
- 🎁 30 积分基础模型余额
- 🎁 3 积分高级模型余额
- 🎁 3 积分绘画余额
还不快来试试?
点击链接,开启你的 AI 创作之旅!>>>https://www.banbanai.cn
板板 AI,让你的工作效果发挥无限可能! 🚀
