解决MinIO 客户端直传架构在反向代理部署下预签名 URL 失效问题
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import 'server-only'
|
||||
import { Client } from 'minio';
|
||||
|
||||
// 初始化 MinIO 客户端
|
||||
// 内部客户端:用于 statObject、removeObject 等服务端直连操作,以及 presignedPostPolicy
|
||||
export const minioClient = new Client({
|
||||
endPoint: process.env.MINIO_ENDPOINT || 'localhost',
|
||||
port: parseInt(process.env.MINIO_API_PORT || '9000'),
|
||||
@@ -10,6 +10,32 @@ export const minioClient = new Client({
|
||||
secretKey: process.env.MINIO_ROOT_PASSWORD || '',
|
||||
});
|
||||
|
||||
/**
|
||||
* 公网客户端:用于生成 presigned GET/PUT URL
|
||||
*
|
||||
* AWS Signature V4 会将 Host 头纳入签名计算,因此 presigned GET/PUT URL
|
||||
* 必须使用与浏览器访问一致的公网地址生成,否则签名校验失败。
|
||||
* 而 POST Policy 不含 Host 签名,只需替换返回的 URL 地址即可。
|
||||
*/
|
||||
function createPresignClient(): Client {
|
||||
const serverUrl = process.env.MINIO_SERVER_URL;
|
||||
if (!serverUrl) return minioClient;
|
||||
|
||||
const parsed = new URL(serverUrl);
|
||||
const useSSL = parsed.protocol === 'https:';
|
||||
const port = parsed.port ? parseInt(parsed.port) : (useSSL ? 443 : 80);
|
||||
|
||||
return new Client({
|
||||
endPoint: parsed.hostname,
|
||||
port,
|
||||
useSSL,
|
||||
accessKey: process.env.MINIO_ROOT_USER || '',
|
||||
secretKey: process.env.MINIO_ROOT_PASSWORD || '',
|
||||
});
|
||||
}
|
||||
|
||||
const presignClient = createPresignClient();
|
||||
|
||||
export const BUCKET_NAME = process.env.MINIO_BUCKET || 'app-files';
|
||||
|
||||
/**
|
||||
@@ -166,6 +192,9 @@ export async function generatePresignedPostPolicy(
|
||||
// 精确匹配
|
||||
policy.setContentType(allowedContentType);
|
||||
}
|
||||
} else {
|
||||
// 未指定类型限制时,允许任意 Content-Type(客户端上传时会设置此字段,Policy 中必须声明)
|
||||
policy.policy.conditions.push(['starts-with', '$Content-Type', '']);
|
||||
}
|
||||
|
||||
if (allowOriginalFilename) {
|
||||
@@ -255,8 +284,8 @@ export async function generatePresignedGetObject(
|
||||
}
|
||||
}
|
||||
|
||||
// 生成预签名 URL
|
||||
const url = await minioClient.presignedGetObject(
|
||||
// 使用公网客户端生成预签名 URL,确保 V4 签名中的 Host 与浏览器访问地址一致
|
||||
const url = await presignClient.presignedGetObject(
|
||||
BUCKET_NAME,
|
||||
objectName,
|
||||
expirySeconds,
|
||||
@@ -264,7 +293,7 @@ export async function generatePresignedGetObject(
|
||||
);
|
||||
|
||||
return {
|
||||
url: replaceUrlBase(url),
|
||||
url,
|
||||
expiresIn: expirySeconds,
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user