当前的代码中,业务平台采用的是OSS,公有云版本的对象存储,如果做私有化交互,客户可能是在私网中,无法访问OSS或者指定使用别的厂商的对象存储,需要有平替的方案。

public interface FileTemplate {
// 基础文件操作
String upload(InputStream inputStream, String objectName, String contentType);
String upload(MultipartFile file, String pathType);
String uploadBase64Image(String base64Data, String pathType);
// URL和流式上传
CompletableFuture<UploadTaskResult> uploadFromUrl(String sourceUrl, UploadOptions options);
String uploadFromStream(InputStream stream, String pathType, String extension);
// 文件管理
FileInfo getFileInfo(String objectName);
boolean deleteFile(String objectName);
String copyFile(String sourceUrl, String targetPath);
// 凭证管理
StsCredential getStsCredential(String path, long expireSeconds, String country);
UploadCredential getUploadCredential(String path, Date expireDate, long maxBytes);
AssumeRoleCredential getAssumeRoleCredential(String sessionName, String allowedPath, int expireSeconds);
// 媒体处理
String extractVideoThumbnail(String videoUrl, int timestamp);
MediaMetadata getMediaMetadata(String mediaUrl);
// 健康检查
boolean isHealthy();
StorageMetrics getMetrics();
}@Component
@ConditionalOnProperty(name = "storage.provider", havingValue = "aliyun-oss")
public class AliyunOssAdapter implements FileTemplate {
private final OSS ossClient;
private final Client iceClient;
private final AliyunOssProperties properties;
@Override
public String upload(InputStream inputStream, String objectName, String contentType) {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
metadata.setObjectAcl(CannedAccessControlList.PublicRead);
PutObjectRequest request = new PutObjectRequest(
properties.getBucketName(), objectName, inputStream, metadata);
ossClient.putObject(request);
return buildPublicUrl(objectName);
}
@Override
public String uploadBase64Image(String base64Data, String pathType) {
// 解析base64数据
String imageData = extractBase64Data(base64Data);
byte[] bytes = Base64.decodeBase64(imageData);
// 检测图片格式
String extension = detectImageFormat(bytes);
if (StringUtils.isBlank(extension)) {
throw new IllegalArgumentException("不支持的图片格式");
}
String objectName = generateObjectName(pathType, extension);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
return upload(inputStream, objectName, "image/" + extension);
} catch (IOException e) {
throw new RuntimeException("上传失败", e);
}
}
@Override
public CompletableFuture<UploadTaskResult> uploadFromUrl(String sourceUrl, UploadOptions options) {
return CompletableFuture.supplyAsync(() -> {
try {
// 创建ICE媒体上传任务
CreateUploadMediaRequest request = new CreateUploadMediaRequest();
request.setAppId(options.getAppName());
request.setFileInfo(buildFileInfo(sourceUrl, options));
request.setUserData(JsonUtils.toJson(options.getUserInfo()));
CreateUploadMediaResponse response = iceClient.createUploadMedia(request);
return UploadTaskResult.builder()
.jobId(response.getBody().getRequestId())
.mediaId(response.getBody().getMediaId())
.status("PENDING")
.build();
} catch (Exception e) {
log.error("URL上传失败: {}", sourceUrl, e);
return UploadTaskResult.failed(e.getMessage());
}
});
}
@Override
public StsCredential getStsCredential(String path, long expireSeconds, String country) {
try {
AssumeRoleRequest request = new AssumeRoleRequest();
request.setDurationSeconds(expireSeconds);
request.setRoleSessionName(generateSessionName());
request.setRoleArn(properties.getRoleArn());
request.setPolicy(buildUploadPolicy(path, expireSeconds));
AssumeRoleResponse response = stsClient.getAcsResponse(request);
return StsCredential.builder()
.accessKeyId(response.getCredentials().getAccessKeyId())
.accessKeySecret(response.getCredentials().getAccessKeySecret())
.securityToken(response.getCredentials().getSecurityToken())
.endpoint(selectEndpoint(country))
.bucketName(properties.getBucketName())
.path(path)
.expiration(response.getCredentials().getExpiration())
.build();
} catch (Exception e) {
throw new RuntimeException("获取STS凭证失败", e);
}
}
@Override
public String extractVideoThumbnail(String videoUrl, int timestamp) {
try {
String thumbnailUrl = videoUrl + String.format(
"?x-oss-process=video/snapshot,t_%d,f_jpg,w_0,h_0,m_fast,ar_auto", timestamp);
URL url = new URL(thumbnailUrl);
try (InputStream inputStream = url.openConnection().getInputStream()) {
String objectName = generateThumbnailName();
return upload(inputStream, objectName, "image/jpeg");
}
} catch (Exception e) {
log.error("提取视频缩略图失败: {}", videoUrl, e);
return null;
}
}
private String selectEndpoint(String country) {
if (StringUtils.isNotBlank(country) && !"CN".equalsIgnoreCase(country)) {
return properties.getAccelerateEndpoint();
}
return properties.getEndpoint();
}
private String buildPublicUrl(String objectName) {
return properties.getReadEndpoint() + "/" + objectName;
}
}@Component
@ConditionalOnProperty(name = "storage.provider", havingValue = "huawei-obs")
public class HuaweiObsAdapter implements FileTemplate {
private final ObsClient obsClient;
private final HuaweiObsProperties properties;
@Override
public String upload(InputStream inputStream, String objectName, String contentType) {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(contentType);
PutObjectRequest request = new PutObjectRequest(
properties.getBucketName(), objectName, inputStream, metadata);
request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
obsClient.putObject(request);
return buildPublicUrl(objectName);
}
@Override
public StsCredential getStsCredential(String path, long expireSeconds, String country) {
// 华为云STS实现
try {
CreateTemporaryAccessKeyByTokenRequest request = new CreateTemporaryAccessKeyByTokenRequest();
request.setDurationSeconds((int) expireSeconds);
// 构建权限策略
Policy policy = buildObsPolicy(path, expireSeconds);
request.setPolicy(JsonUtils.toJson(policy));
CreateTemporaryAccessKeyByTokenResponse response = iamClient.createTemporaryAccessKeyByToken(request);
return StsCredential.builder()
.accessKeyId(response.getCredential().getAccess())
.accessKeySecret(response.getCredential().getSecret())
.securityToken(response.getCredential().getSecuritytoken())
.endpoint(properties.getEndpoint())
.bucketName(properties.getBucketName())
.path(path)
.expiration(response.getCredential().getExpiresAt())
.build();
} catch (Exception e) {
throw new RuntimeException("获取华为云STS凭证失败", e);
}
}
// 其他方法实现...
}本技术方案通过以下关键设计实现了信创适配文件存储服务的快速切换:

minio版本作为默认实现;
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。