共计 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
[Unit]
Description=MinIO Server // MinIO 服务的名称
Documentation=https://min.io/docs/minio/linux/index.html // 这个无所谓可以随便写,我写了 minio 文档的地址
After=network.target // 在网络连接完成后启动 MinIO 服务
[Service]
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 的根用户和密码
[Install]
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';
@Injectable()
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'
@Controller('minio')
export class MinIOController {constructor(private readonly minioService: MinIOService) {}
@Post('uploadImageOSS')
@UseInterceptors(AnyFilesInterceptor())
async uploadImageOSS(@UploadedFiles() 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,让你的工作效果发挥无限可能! 🚀