package com.xiaopeng.map.service.serviceimpl;
|
|
import com.aliyun.oss.OSS;
|
import com.aliyun.oss.OSSClientBuilder;
|
import com.aliyun.oss.HttpMethod;
|
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
|
import com.aliyun.oss.model.ListObjectsRequest;
|
import com.aliyun.oss.model.OSSObjectSummary;
|
import com.aliyun.oss.model.ObjectListing;
|
import com.aliyun.oss.model.ObjectMetadata;
|
import com.xiaopeng.map.config.OssProperties;
|
import com.xiaopeng.map.service.RobotaxiOssService;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.StringUtils;
|
|
import java.io.ByteArrayInputStream;
|
import java.io.InputStream;
|
import java.net.URL;
|
import java.util.ArrayList;
|
import java.util.Date;
|
import java.util.List;
|
|
/**
|
* ROBOTAXI OSS 服务实现,使用阿里云官方 OSS SDK 上传 ODD 图层文件。
|
* 路径格式:ROBOTAXI_ODD_layer/{环境}/{车辆uuid}/{fileName}
|
*/
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor(onConstructor_ = @Lazy)
|
@ConditionalOnProperty(name = "aliyun.oss.enabled", havingValue = "true")
|
public class RobotaxiOssServiceImpl implements RobotaxiOssService {
|
|
private final OssProperties ossProperties;
|
|
@Override
|
public String uploadOddLayerFile(byte[] data, String fileName, String vehicleUuid) {
|
if (data == null) {
|
throw new IllegalArgumentException("data cannot be null");
|
}
|
return uploadOddLayerFile(new ByteArrayInputStream(data), data.length, fileName, vehicleUuid);
|
}
|
|
@Override
|
public String uploadOddLayerFile(InputStream inputStream, long contentLength, String fileName,
|
String vehicleUuid) {
|
validateParams(fileName, vehicleUuid);
|
|
String objectKey = buildObjectKey(vehicleUuid, fileName);
|
String bucketName = ossProperties.getBucketName();
|
|
OSS ossClient = null;
|
try {
|
ossClient = createOssClient();
|
ObjectMetadata metadata = new ObjectMetadata();
|
metadata.setContentLength(contentLength);
|
|
ossClient.putObject(bucketName, objectKey, inputStream, metadata);
|
log.debug("Uploaded ODD layer file to OSS: {}", objectKey);
|
return objectKey;
|
} finally {
|
if (ossClient != null) {
|
ossClient.shutdown();
|
}
|
}
|
}
|
|
@Override
|
public boolean deleteByObjectKey(String objectKey) {
|
if (!StringUtils.hasText(objectKey)) {
|
throw new IllegalArgumentException("objectKey cannot be empty");
|
}
|
String bucketName = ossProperties.getBucketName();
|
OSS ossClient = null;
|
try {
|
ossClient = createOssClient();
|
ossClient.deleteObject(bucketName, objectKey);
|
log.debug("Deleted OSS object: {}", objectKey);
|
return true;
|
} catch (Exception e) {
|
log.warn("Failed to delete OSS object: {}, error: {}", objectKey, e.getMessage());
|
return false;
|
} finally {
|
if (ossClient != null) {
|
ossClient.shutdown();
|
}
|
}
|
}
|
|
@Override
|
public List<String> deleteByObjectKeys(List<String> objectKeys) {
|
if (objectKeys == null || objectKeys.isEmpty()) {
|
return new ArrayList<>();
|
}
|
String bucketName = ossProperties.getBucketName();
|
OSS ossClient = null;
|
List<String> deleted = new ArrayList<>();
|
try {
|
ossClient = createOssClient();
|
for (String key : objectKeys) {
|
if (!StringUtils.hasText(key)) {
|
continue;
|
}
|
try {
|
ossClient.deleteObject(bucketName, key);
|
deleted.add(key);
|
log.debug("Deleted OSS object: {}", key);
|
} catch (Exception e) {
|
log.warn("Failed to delete OSS object: {}, error: {}", key, e.getMessage());
|
}
|
}
|
return deleted;
|
} finally {
|
if (ossClient != null) {
|
ossClient.shutdown();
|
}
|
}
|
}
|
|
@Override
|
public List<String> deleteOddLayerFilesByPrefix(String vehicleUuid) {
|
validateParamsForPrefix(vehicleUuid);
|
|
String prefix = buildObjectKeyPrefix(vehicleUuid);
|
String bucketName = ossProperties.getBucketName();
|
|
OSS ossClient = null;
|
try {
|
ossClient = createOssClient();
|
List<String> objectKeys = new ArrayList<>();
|
String nextMarker = null;
|
|
do {
|
ListObjectsRequest request = new ListObjectsRequest(bucketName).withPrefix(prefix).withMarker(nextMarker);
|
ObjectListing listing = ossClient.listObjects(request);
|
for (OSSObjectSummary summary : listing.getObjectSummaries()) {
|
objectKeys.add(summary.getKey());
|
}
|
nextMarker = listing.isTruncated() ? listing.getNextMarker() : null;
|
} while (nextMarker != null);
|
|
if (objectKeys.isEmpty()) {
|
return new ArrayList<>();
|
}
|
return deleteByObjectKeys(objectKeys);
|
} finally {
|
if (ossClient != null) {
|
ossClient.shutdown();
|
}
|
}
|
}
|
|
@Override
|
public String generatePresignedDownloadUrl(String objectKey, int expireSeconds) {
|
if (!StringUtils.hasText(objectKey)) {
|
throw new IllegalArgumentException("objectKey cannot be empty");
|
}
|
if (expireSeconds <= 0) {
|
throw new IllegalArgumentException("expireSeconds must be positive");
|
}
|
String bucketName = ossProperties.getBucketName();
|
OSS ossClient = null;
|
try {
|
ossClient = createOssClient();
|
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey, HttpMethod.GET);
|
request.setExpiration(new Date(System.currentTimeMillis() + expireSeconds * 1000L));
|
URL url = ossClient.generatePresignedUrl(request);
|
return url != null ? url.toString() : null;
|
} finally {
|
if (ossClient != null) {
|
ossClient.shutdown();
|
}
|
}
|
}
|
|
private void validateParamsForPrefix(String vehicleUuid) {
|
if (!StringUtils.hasText(vehicleUuid)) {
|
throw new IllegalArgumentException("vehicleUuid cannot be empty");
|
}
|
}
|
|
private String buildObjectKeyPrefix(String vehicleUuid) {
|
String pathPrefix = ossProperties.getPathPrefix();
|
if (!StringUtils.hasText(pathPrefix)) {
|
pathPrefix = "ROBOTAXI_ODD_layer";
|
}
|
String env = StringUtils.hasText(ossProperties.getEnv()) ? ossProperties.getEnv() : "test";
|
return String.format("%s/%s/%s/", pathPrefix, env, vehicleUuid);
|
}
|
|
private void validateParams(String fileName, String vehicleUuid) {
|
if (!StringUtils.hasText(fileName)) {
|
throw new IllegalArgumentException("fileName cannot be empty");
|
}
|
if (!StringUtils.hasText(vehicleUuid)) {
|
throw new IllegalArgumentException("vehicleUuid cannot be empty");
|
}
|
}
|
|
private String buildObjectKey(String vehicleUuid, String fileName) {
|
String pathPrefix = ossProperties.getPathPrefix();
|
if (!StringUtils.hasText(pathPrefix)) {
|
pathPrefix = "ROBOTAXI_ODD_layer";
|
}
|
String env = StringUtils.hasText(ossProperties.getEnv()) ? ossProperties.getEnv() : "test";
|
return String.format("%s/%s/%s/%s", pathPrefix, env, vehicleUuid, fileName);
|
}
|
|
private OSS createOssClient() {
|
String endpoint = ossProperties.getEndpoint();
|
String accessKeyId = ossProperties.getAccessKeyId();
|
String accessKeySecret = ossProperties.getAccessKeySecret();
|
|
if (!StringUtils.hasText(endpoint) || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
throw new IllegalStateException("OSS config incomplete: endpoint, accessKeyId, accessKeySecret must be set");
|
}
|
|
return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
|
}
|
|
/**
|
* 构建 OSS 对象访问 URL:https://{bucket}.{endpoint}/{objectKey}
|
*/
|
private String buildObjectUrl(String bucketName, String objectKey) {
|
String endpoint = ossProperties.getEndpoint();
|
if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
|
int schemeEnd = endpoint.indexOf("://") + 3;
|
endpoint = endpoint.substring(schemeEnd);
|
}
|
return String.format("https://%s.%s/%s", bucketName, endpoint, objectKey);
|
}
|
}
|