当前位置: 首页 > news >正文

华润对象存储(OBS)工具类

目录

    • 一、备注
    • 二、工具类
    • 三、对象存储放在内网,如何实现外网访问


一、备注

1、ObjectBasicInfo、ObjectDetailInfo、ResultBody这三个类可自行替换或者去掉


二、工具类

package com.xxx.util;import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.xxx.entity.huarun.ObjectBasicInfo;
import com.xxx.entity.huarun.ObjectDetailInfo;
import com.xxx.entity.huarun.ResultBody;
import com.hitachivantara.common.util.DatetimeFormat;
import com.hitachivantara.core.http.Protocol;
import com.hitachivantara.core.http.client.ClientConfiguration;
import com.hitachivantara.hcp.build.HCPClientBuilder;
import com.hitachivantara.hcp.build.HCPNamespaceClientBuilder;
import com.hitachivantara.hcp.common.auth.LocalCredentials;
import com.hitachivantara.hcp.standard.api.HCPNamespace;
import com.hitachivantara.hcp.standard.api.event.ListObjectHandler;
import com.hitachivantara.hcp.standard.define.NextAction;
import com.hitachivantara.hcp.standard.model.HCPObject;
import com.hitachivantara.hcp.standard.model.HCPObjectSummary;
import com.hitachivantara.hcp.standard.model.request.impl.CopyObjectRequest;
import com.hitachivantara.hcp.standard.model.request.impl.ListObjectRequest;
import com.obs.services.ObsClient;
import com.obs.services.model.HeaderResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.web.multipart.MultipartFile;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;/*** 华润对象存储(OBS)-工具类** @author hcs* @date 2023/6/20 17:03*/
@Slf4j
public class HuaRunOBSUtil {/*** 文件外链过期时间,7天*/private static long expire = 7 * 24 * 60 * 60;/*** 文件外链访问端口*/private static String port = "";private static RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);private static String bucketName;private static String ak;private static String sk;private static String endPoint;private static String targetDomainName;private static String replaceDomainName;/*** OBS操作客户端*/private static HCPNamespace obsClient = null;/*** 上传、下载文件时使用以下对象** 解决报错:Received fatal alert: protocol_version** 华润对象存储的服务器支持TLSv1.2(使用tcpdump命令抓包看到的)*/private static AmazonS3 S3APIClient;//private static AmazonS3 hs3Client;/*** OBS操作客户端Map,key=bucketName,value=客户端*/private static Map<String, HCPNamespace> obsClientMap = new HashMap<>();private static final String SEPARATOR = "/";public HuaRunOBSUtil(String bucketName, String ak, String sk, String endPoint, String port) {HuaRunOBSUtil.bucketName = bucketName;HuaRunOBSUtil.ak = ak;HuaRunOBSUtil.sk = sk;HuaRunOBSUtil.endPoint = endPoint;if (StringUtils.isNotBlank(port)) {HuaRunOBSUtil.port = ":" + port;}createObsClientInstance();S3APIClient = getInstance(endPoint, ak, sk, "S3SignerType", getHttpProtocol(endPoint));}public static String getBucketName() {return bucketName;}public static String getAk() {return ak;}public static String getSk() {return sk;}public static String getEndPoint() {return endPoint;}public static synchronized AmazonS3 getInstance(String endpoint, String ak, String sk,String signature, String protocol) {synchronized (AmazonS3.class) {if (null == S3APIClient) {S3APIClient = getHCPCSS3Client(endpoint, ak, sk, signature, protocol);}}return S3APIClient;}/*** 获取亚马逊S3客户端** @param endpoint* @param ak* @param sk* @param signature     S3SignerType - 表示V2*                      AWSS3V4SignerType - 表示V4* @param protocol* @return*/private static AmazonS3 getHCPCSS3Client(String endpoint, String ak, String sk, String signature, String protocol) {log.info("开始\t创建S3客户端");com.amazonaws.ClientConfiguration clientConfig = new com.amazonaws.ClientConfiguration();//使用HTTP或HTTPS协议if (protocol.toUpperCase().equals("HTTP")) {clientConfig.setProtocol(com.amazonaws.Protocol.HTTP);} else {clientConfig.setProtocol(com.amazonaws.Protocol.HTTPS);try {SSLContextBuilder builder = new SSLContextBuilder();builder.loadTrustMaterial(null, new TrustStrategy() {public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {return true;}});SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),/*** 仅指定TLSv1.2协议,解决报错:Received fatal alert: protocol_version** 如果加上其他版本的协议,有可能会报错:Received fatal alert: protocol_version** 例如指定为TLSv1.1,使用tcpdump命令抓包时如果发现TLSv1.2,则该次交互时失败的*/new String[]{"TLSv1.2"},// For Java 1.7 , 1.8//new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"},// For Java1.6-1.7//new String[] { "TLSv1" },new String[]{"TLS_RSA_WITH_AES_128_CBC_SHA"},NoopHostnameVerifier.INSTANCE);clientConfig.getApacheHttpClientConfig().setSslSocketFactory(sslsf);} catch (Exception e) {log.error("创建S3客户端出现异常:" + e.getMessage(), e);}}//连接池的连接数clientConfig.setMaxConnections(50);//V2签名 或 V4签名//S3SignerType - 表示V2//AWSS3V4SignerType - 表示V4clientConfig.setSignerOverride(signature);AmazonS3 client = AmazonS3ClientBuilder.standard().withClientConfiguration(clientConfig)/*** 以下配置为false:外链格式:存储桶.endpoint/目录/文件名* 以下配置为true:外链格式:endpoint/存储桶/目录/文件名*///.withPathStyleAccessEnabled(true).withEndpointConfiguration(new com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration(endpoint, null)).withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(ak, sk))).build();log.info("成功\t创建S3客户端");return client;}/*** 获取OBS操作客户端** @return*/private static void createObsClientInstance() {try {if (obsClient == null) {synchronized (ObsClient.class) {if (obsClient == null) {log.info("createObsClientInstance--->bucketName={},ak={},sk={},endPoint={}", bucketName, ak, sk, endPoint);ClientConfiguration clientConfig = new ClientConfiguration();String httpProtocol = getHttpProtocol(endPoint);clientConfig.setProtocol(httpProtocol.equalsIgnoreCase(Protocol.HTTPS.toString()) ? Protocol.HTTPS : Protocol.HTTP);HCPNamespaceClientBuilder builder = HCPClientBuilder.defaultHCPClient();obsClient = builder.withClientConfiguration(clientConfig).withCredentials(new LocalCredentials(ak, sk))// 去除http协议.withEndpoint(endPoint.replace(getHttpProtocol(endPoint) + "://", "")).withNamespace(bucketName).bulid();obsClientMap.put(bucketName, obsClient);}}}} catch (Exception e) {log.error("连接华润对象存储服务器异常:" + e.getMessage(), e);}}/*** 获取上传文件的基础路径** @return url*/public static String getBasisUrl() {//实示例:http协议 + 存储桶名称 + . + endPoint + port + /String basisUrl = getHttpProtocol(endPoint) + "://" + bucketName + "." + endPoint.replace(getHttpProtocol(endPoint) + "://", "") + (StringUtils.isNotBlank(port) ? port + SEPARATOR : "");log.info("getBasisUrl--->" + basisUrl);basisUrl = replaceDomainName(basisUrl);return basisUrl;}/*** 获取上传文件的基础路径** @param bucketName* @return*/public static String getBasisUrl(String bucketName) {//实示例:http协议 + 存储桶名称 + . + endPoint + port + /String basisUrl = getHttpProtocol(endPoint) + "://" + bucketName + "." + endPoint.replace(getHttpProtocol(endPoint) + "://", "") + (StringUtils.isNotBlank(port) ? port + SEPARATOR : "");log.info("getBasisUrl2--->" + basisUrl);basisUrl = replaceDomainName(basisUrl);return basisUrl;}/*** 根据url地址获取存储桶名称** @param url* @return*/public static String getBucketNameByUrl(String url) {if (StringUtils.isNotBlank(url) && StringUtils.isNotBlank(getHttpProtocol(endPoint))&& url.contains(endPoint.replace(getHttpProtocol(endPoint) + "://", ""))) {String str = url.replace(getHttpProtocol(endPoint) + "://", "");return str.substring(0, str.indexOf("."));} else {// 华润只会使用一个存储桶(访问外链替换了域名导致不支持多个存储桶),这里直接返回log.info("getBucketNameByUrl--->返回默认存储桶" + HuaRunOBSUtil.bucketName);return HuaRunOBSUtil.bucketName;}}/*** 获取区域** @param endPoint* @return*/public static String getRegion(String endPoint) {String substring = endPoint.substring(endPoint.indexOf(".") + 1);return substring.substring(0, substring.indexOf("."));}/*** 获取http协议** @param endPoint* @return*/public static String getHttpProtocol(String endPoint) {return endPoint.substring(0, endPoint.indexOf(":"));}/*** 创建存储桶(不确定以下方式是否有用)** @param bucketName* @return*/public static void createBucket(String bucketName, String endPoint) {try {log.info("createBucket.bucketName--->" + bucketName);log.info("createBucket.endPoint--->" + endPoint);if (!headBucket(bucketName)) {ClientConfiguration clientConfig = new ClientConfiguration();String httpProtocol = getHttpProtocol(endPoint);clientConfig.setProtocol(httpProtocol.equalsIgnoreCase(Protocol.HTTPS.toString()) ? Protocol.HTTPS : Protocol.HTTP);HCPNamespaceClientBuilder builder = HCPClientBuilder.defaultHCPClient();HCPNamespace obsClient = builder.withClientConfiguration(clientConfig).withCredentials(new LocalCredentials(ak, sk))// 去除http协议.withEndpoint(endPoint.replace(getHttpProtocol(endPoint) + "://", "")).withNamespace(bucketName).bulid();obsClientMap.put(bucketName, obsClient);}} catch (Exception e) {log.error("createBucket出现异常:" + e.getMessage(), e);}}/*** 创建存储桶** @param bucketName* @return*/public static void createBucket(String bucketName) {try {log.info("createBucket.bucketName--->" + bucketName);if (!headBucket(bucketName)) {S3APIClient.createBucket(bucketName);}} catch (Exception e) {log.error("createBucket出现异常:" + e.getMessage(), e);}}/*** 删除存储桶** @param bucketName* @return*/public static void deleteBucket(String bucketName) {S3APIClient.deleteBucket(bucketName);}/*** 判断存储桶是否存在** @param bucketName* @return*/public static boolean headBucket(String bucketName) {try {return obsClient.doesNamespacesExist(bucketName);} catch (Exception e) {log.error("headBucket出现异常:" + e.getMessage(), e);}return false;}/*** 上传字符** @param bucketName* @param objectName* @param content* @return*/public static ResultBody putObjectByStr(String bucketName, String objectName, String content) {log.info("putObjectByStr--->bucketName={},objectName={},content={}", bucketName, objectName, content);if (StringUtils.isBlank(content)) {return null;}//重新构建objectNameobjectName = buildObjectName(objectName);String errorMsg = "";for (int i = 0; i < 3; i++) {try {S3APIClient.putObject(bucketName, objectName, content);return ResultBody.success(new ObjectBasicInfo("putObjectByStr", objectName));} catch (Exception e) {log.error("putObjectByStr出现异常:" + e.getMessage(), e);errorMsg = e.getMessage();}}return ResultBody.failure(errorMsg);}/*** 上传输入流** @param bucketName* @param objectName* @param inputStream* @return*/public static ResultBody putObjectByInput(String bucketName, String objectName, InputStream inputStream) {log.info("putObjectByInput--->bucketName={},objectName={}", bucketName, objectName);//重新构建objectNameobjectName = buildObjectName(objectName);String errorMsg = "";ObjectMetadata metadata = new ObjectMetadata();for (int i = 0; i < 3; i++) {try {metadata.setContentLength(inputStream.available());S3APIClient.putObject(bucketName, objectName, inputStream, metadata);return ResultBody.success(new ObjectBasicInfo("inputStream", objectName));} catch (Exception e) {log.error("第" + (i + 1) + "次putObjectByInput出现异常:" + e.getMessage(), e);errorMsg = e.getMessage();}}return ResultBody.failure(errorMsg);}/*** 上传文件输入流** @param bucketName* @param objectName* @param fileInputStream* @return*/public static ResultBody putObjectByFileInput(String bucketName, String objectName, FileInputStream fileInputStream) {log.info("putObjectByFileInput--->bucketName={},objectName={}", bucketName, objectName);return putObjectByInput(bucketName, objectName, fileInputStream);}/*** 通过MultipartFile,上传文件** @param bucketName* @param objectName* @param media* @return*/public static ResultBody putObjectByMultipartFile(String bucketName, String objectName, MultipartFile media) throws IOException {log.info("putObjectByMultipartFile--->bucketName={},objectName={}", bucketName, objectName);return putObjectByInput(bucketName, objectName, media.getInputStream());}/*** 上传本地文件** @param bucketName* @param objectName* @param file* @return*/public static ResultBody putObjectByFile(String bucketName, String objectName, File file) {log.info("putObjectByFile--->bucketName={},objectName={}", bucketName, objectName);//重新构建objectNameobjectName = buildObjectName(objectName);String errorMsg = "";for (int i = 0; i < 3; i++) {try {S3APIClient.putObject(bucketName, objectName, file);return ResultBody.success(new ObjectBasicInfo(file.getName(), objectName));} catch (Exception e) {log.error("第" + (i + 1) + "次putObjectByFile出现异常:" + e.getMessage(), e);errorMsg = e.getMessage();}}return ResultBody.failure(errorMsg);}/*** 下载文件到本地** @param bucketName* @param objectName* @param filePath* @return*/public static boolean downloadObject(String bucketName, String objectName, String filePath) throws Exception {log.info("downloadObject--->bucketName={},objectName={},filePath={}", bucketName, objectName, filePath);if (StringUtils.isBlank(filePath)) {return false;}//重新构建objectNameobjectName = buildObjectName(objectName);filePath = filePath.replace("\\", SEPARATOR);InputStream input = null;FileOutputStream fileOutputStream = null;try {S3Object obsObject = null;for (int i = 0; i < 3; i++) {try {// 获取对象obsObject = S3APIClient.getObject(bucketName, objectName);break;} catch (Exception e) {log.error("第" + (i + 1) + "次downloadObject出现异常:" + e.getMessage(), e);}}// 读取对象内容input = obsObject.getObjectContent();if (input == null) {return false;}//获取文件夹路径if (filePath.contains(SEPARATOR)) {String dir = filePath.substring(0, filePath.lastIndexOf(SEPARATOR));File difFile = new File(dir);if (!difFile.exists()) {//创建文件夹boolean mkdirs = difFile.mkdirs();}}File file = new File(filePath);fileOutputStream = new FileOutputStream(file);byte[] b = new byte[1024];int len;while ((len = input.read(b)) != -1) {fileOutputStream.write(b, 0, len);}return true;} finally {if (fileOutputStream != null) {fileOutputStream.close();}if (input != null) {input.close();}}}/*** 获取文件内容** @param bucketName* @param objectName* @return*/public static String getObjectContent(String bucketName, String objectName) throws IOException {log.info("getObjectContent--->bucketName={},objectName={}", bucketName, objectName);//重新构建objectNameobjectName = buildObjectName(objectName);InputStream input = null;ByteArrayOutputStream bos = new ByteArrayOutputStream();try {for (int i = 0; i < 3; i++) {try {// 读取对象内容input = S3APIClient.getObject(bucketName, objectName).getObjectContent();break;} catch (Exception e) {log.error("第" + (i + 1) + "次getObjectContent出现异常:" + e.getMessage(), e);}}byte[] b = new byte[1024];int len;while ((len = input.read(b)) != -1) {bos.write(b, 0, len);}return new String(bos.toByteArray());} catch (Exception e) {log.error("getObjectContent出现异常:" + e.getMessage(), e);} finally {bos.close();if (input != null) {input.close();}}return null;}/*** 获取文件输入流** @param bucketName* @param objectName* @return*/public static InputStream getObject(String bucketName, String objectName) {log.info("getObject--->bucketName={},objectName={}", bucketName, objectName);//重新构建objectNameobjectName = buildObjectName(objectName);for (int i = 0; i < 3; i++) {try {S3Object object = S3APIClient.getObject(bucketName, objectName);return object.getObjectContent();} catch (Exception e) {log.error("第" + (i + 1) + "次getObject出现异常:" + e.getMessage(), e);}}return null;}/*** 列举指定目录的全部对象** @param bucketName* @param directoryPath* @return*/public static ResultBody listAllObjects(String bucketName, String directoryPath) {log.info("listAllObjects--->bucketName={},directoryPath={}", bucketName, directoryPath);List<ObjectDetailInfo> objList = new ArrayList<>();try {HCPNamespace obsClient = obsClientMap.get(bucketName);ListObjectRequest request = new ListObjectRequest(directoryPath).withRecursiveDirectory(true);obsClient.listObjects(request, new ListObjectHandler() {@Overridepublic NextAction foundObject(HCPObjectSummary obj) {ObjectDetailInfo objInfo = new ObjectDetailInfo(obj.getName(),obj.getKey(),obj.getSize(),obj.getType(),DatetimeFormat.ISO8601_DATE_FORMAT.format(new Date(obj.getChangeTime())),obj.getContentHash());objList.add(objInfo);return null;}});} catch (Exception e) {log.error("listAllObjects出现异常:" + e.getMessage(), e);return ResultBody.failure(e.getMessage());}return ResultBody.success(objList);}/*** 删除单个对象** @param bucketName* @param objectName* @return*/public static ResultBody deleteObject(String bucketName, String objectName) {log.info("deleteObject--->bucketName={},objectName={}", bucketName, objectName);//重新构建objectNameobjectName = buildObjectName(objectName);try {HCPNamespace obsClient = obsClientMap.get(bucketName);obsClient.deleteObject(bucketName, objectName);return ResultBody.success();} catch (Exception e) {log.error("deleteObject出现异常:" + e.getMessage(), e);return ResultBody.failure(e.getMessage());}}/*** 复制对象** @param sourceBucketName* @param sourceObjectName* @param destBucketName* @param destObjectName* @return*/public static boolean copyObject(String sourceBucketName, String sourceObjectName,String destBucketName, String destObjectName) {log.info("deleteObject--->sourceBucketName={},sourceObjectName={},destBucketName={},destObjectName={}", bucketName, sourceObjectName, destBucketName, destObjectName);CopyObjectRequest copyObjectRequest = new CopyObjectRequest();copyObjectRequest.withSourceNamespace(sourceBucketName);copyObjectRequest.withSourceKey(sourceObjectName);copyObjectRequest.withTargetNamespace(destBucketName);copyObjectRequest.withTargetKey(destObjectName);try {obsClient.copyObject(copyObjectRequest);return true;} catch (Exception e) {log.error("copyObject出现异常:" + e.getMessage(), e);}return false;}/*** 判断对象是否存在** @param bucketName* @param objectName* @return*/public static boolean doesObjectExist(String bucketName, String objectName) {log.info("deleteObject--->bucketName={},objectName={}", bucketName, objectName);//重新构建objectNameobjectName = buildObjectName(objectName);try {HCPNamespace obsClient = obsClientMap.get(bucketName);return obsClient.doesObjectExist(objectName);} catch (Exception e) {log.error("doesObjectExist出现异常:" + e.getMessage(), e);}return false;}/*** 获取文件外链** @param bucketName* @param objectName* @param expires    单位:秒(s)* @return*/public static String getSignedUrl(String bucketName, String objectName, Long expires) {log.info("getSignedUrl--->bucketName={},objectName={},expires={}", bucketName, objectName, expires);//重新构建objectNameobjectName = buildObjectName(objectName);Date expiration = new Date(System.currentTimeMillis() + (expires * 1000));try {//String e = endPoint.replace(getHttpProtocol(endPoint) + "://", "");//AmazonS3 hs3Client = newS3Client(e, ak, sk);// 生成预签名时间URL url = S3APIClient.generatePresignedUrl(new GeneratePresignedUrlRequest(bucketName, objectName).withExpiration(expiration).withMethod(HttpMethod.GET));String s = url.toString();log.info("这个是api生成的url------------------------------------>" + s);// 由于api返回的是http,这里自行替换为endpoint使用的协议s = s.replace(getHttpProtocol(s) + "://", getHttpProtocol(endPoint) + "://");log.info("替换协议后url------>" + s);s = replaceDomainName(s);return s;} catch (Exception e) {log.error("getSignedUrl出现异常:" + e.getMessage(), e);}return null;}/*** 获取文件外链-url有效时间默认7天** @param bucketName* @param objectName* @return*/public static String getSignedUrl(String bucketName, String objectName) {return getSignedUrl(bucketName, objectName, expire);}/*** 重新构建objectName** @param objectName*/private static String buildObjectName(String objectName) {if (StringUtils.isBlank(objectName)) {return objectName;}//去除开头的/objectName = objectName.startsWith("/") ? objectName.substring(1) : objectName;//去除?后边的参数objectName = objectName.contains("?") ? objectName.substring(0, objectName.indexOf("?")) : objectName;return objectName;}/*** 传入文件访问外链,返回objectName** @param url* @return*/public static String getObjectNameByUrl(String url) {if (StringUtils.isBlank(url)) {return url;}try {url = URLDecoder.decode(url, "UTF-8");} catch (Exception e) {log.error("getObjectNameByUrl获取文件外链失败:" + e.getMessage(), e);}if (url.contains(getBasisUrl())) {// 去除基础路径url = url.replace(getBasisUrl(), "");// 去除?后边的参数url = url.contains("?") ? url.substring(0, url.indexOf("?")) : url;}return url;}/*** 获取华润OBS文件外链** @param redisKey* @param objectName* @return* @throws Exception*/public static String getOBSFileUrl(String redisKey, String objectName) throws Exception {if (StringUtils.isBlank(objectName) || StringUtils.isBlank(redisKey) || OBSUtil.httpRegex(objectName)) {return "";}objectName = URLDecoder.decode(objectName, "UTF-8");//重新构建objectNameobjectName = buildObjectName(objectName);Object o = redisUtil.get(redisKey);String url = o == null ? null : o.toString();if (StringUtils.isNotBlank(url)) {return url;} else {//重新获取urlString signedUrl = getSignedUrl(getBucketName(), objectName, expire);//保存访问url,有效期为6天redisUtil.set(redisKey, signedUrl, 6 * 24 * 60 * 60);return signedUrl;}}/*** 从指定存储桶获取华润OBS文件外链** @param redisKey* @param objectName* @param bucketName* @return* @throws Exception*/public static String getOBSFileUrl(String redisKey, String objectName, String bucketName) throws Exception {if (StringUtils.isBlank(objectName) || StringUtils.isBlank(redisKey) || StringUtils.isBlank(bucketName)) {return "";}//重新构建objectNameobjectName = buildObjectName(objectName);Object o = redisUtil.get(redisKey);String url = o == null ? null : o.toString();if (StringUtils.isNotBlank(url)) {return url;} else {//重新获取urlString signedUrl = getSignedUrl(bucketName, objectName, expire);//保存访问url,有效期为6天redisUtil.set(redisKey, signedUrl, 6 * 24 * 60 * 60);return signedUrl;}}/*** 保存华润OBS文件外链至redis** @param redisKey* @param url* @return*/public static boolean setOBSFileUrl(String redisKey, String url) {Object o = redisUtil.get(redisKey);if (o != null) {String value = o.toString();//只保存新的外链,解决url重复保存导致redis保存的是过期外链的问题if (!value.equals(url)) {//保存访问url,有效期为6天return redisUtil.set(redisKey, url, 6 * 24 * 60 * 60);}} else {//保存访问url,有效期为6天return redisUtil.set(redisKey, url, 6 * 24 * 60 * 60);}return true;}/*** 删除华润OBS文件外链** @param redisKey*/public static void delOBSFileUrl(String redisKey) {//删除访问urlredisUtil.del(redisKey);}/*** 替换域名** @param url* @return*/private static String replaceDomainName(String url){if (StringUtils.isNotBlank(replaceDomainName) && StringUtils.isNotBlank(targetDomainName)) {url = url.replace(targetDomainName, replaceDomainName);log.info("替换域名后url------>" + url);}return url;}public static void setTargetDomainName(String targetDomainName) {HuaRunOBSUtil.targetDomainName = targetDomainName;}public static void setReplaceDomainName(String replaceDomainName) {HuaRunOBSUtil.replaceDomainName = replaceDomainName;}
}

三、对象存储放在内网,如何实现外网访问

如果对象存储是放在内网的,可以按照以下示例配置ngin代理

nginx配置请参考:https://app.rwork.crc.com.cn/docs/dock9zVHp5WcpLYNKfjo5ACbU8g

结合代码中的targetDomainNamereplaceDomainName可实现外网访问

相关文章:

华润对象存储(OBS)工具类

目录 一、备注二、工具类三、对象存储放在内网&#xff0c;如何实现外网访问 一、备注 1、ObjectBasicInfo、ObjectDetailInfo、ResultBody这三个类可自行替换或者去掉 二、工具类 package com.xxx.util;import com.amazonaws.HttpMethod; import com.amazonaws.auth.AWSStat…...

强缓存和协商缓存的区别?

协商缓存和强缓存是 HTTP 缓存机制中的两种不同的策略&#xff0c;用于减少网络请求并提高网页加载速度。它们之间的主要区别在于缓存的验证方式和服务器返回的响应头。 强缓存&#xff1a; 强缓存是基于过期时间&#xff08;Expires&#xff09;和缓存标识&#xff08;Cache…...

ChatGPT提问技巧——对抗性提示

ChatGPT提问技巧——对抗性提示 对抗性提示是一种允许模型生成能够抵御某些类型的攻击或偏差的文本的技术。这种技术可用于训练更健壮、更能抵御某些类型的攻击或偏差的模型。 要在 ChatGPT 中使用对抗性提示&#xff0c;应为模型提供一个提示&#xff0c;该提示的设计应使模…...

openGauss使用BenchmarkSQL进行性能测试(上)

一、前言 本文提供openGauss使用BenchmarkSQL进行性能测试的方法和测试数据报告。 BenchmarkSQL&#xff0c;一个JDBC基准测试工具&#xff0c;内嵌了TPC-C测试脚本&#xff0c;支持很多数据库&#xff0c;如PostgreSQL、Oracle和Mysql等。 TPC-C是专门针对联机交易处理系统…...

Java的线程池机制

Java的线程池机制是用来管理和调度多个线程的工具。通过线程池&#xff0c;可以避免频繁地创建和销毁线程&#xff0c;提高线程的复用率&#xff0c;减少资源消耗。 Java中提供了几种不同类型的线程池&#xff1a; 1、FixedThreadPool&#xff08;固定大小线程池&#xff09;…...

EasyCode 插件的具体使用

前言 EasyCode 是基于IntelliJ IDEA Ultimate版开发的一个代码生成插件&#xff0c;主要通过自定义模板&#xff08;基于velocity&#xff09;来生成各种你想要的代码。通常用于生成Entity、Dao、Service、Controller。如果你动手能力强还可以用于生成HTML、JS、PHP等代码。理…...

Ypay源支付6.9无授权聚合免签系统可运营源码

YPay是一款专为个人站长设计的聚合免签系统&#xff0c;YPay基于高性能的ThinkPHP 6.1.2 Layui PearAdmin架构&#xff0c;提供了实时监控和管理的功能&#xff0c;让您随时随地掌握系统运营情况。 说明 Ypay源支付6.9无授权聚合免签系统可运营源码 已搭建测试无加密版本…...

SpringBoot+Vue项目报错(问题已解决)

1、错误日志 2、分析原因&#xff1a; JWT strings must contain exactly 2 period characters. Found: 0 JWT字符串必须包含2个句号字符。发现:0 分析&#xff1a;可以判断出大概可能是token格式出现了问题 3、参考 http://t.csdnimg.cn/hfEiY 4、检查后端代码是否出现问…...

DEAP 自定义交叉操作

在遗传算法中&#xff0c;使用DEAP库来实现自定义的交叉操作可以非常灵活。如果你想模拟多个染色体的情况&#xff0c;通过在染色体的特定区间进行交叉&#xff0c;你需要自定义一个交叉函数。以下是一个示例&#xff0c;展示如何实现一个自定义的交叉函数&#xff0c;该函数允…...

ByText

getByText, queryByText, getAllByText, queryAllByText, findByText, findAllByText API​ getByText(// If youre using screen, then skip the container argument:container: HTMLElement,text: TextMatch,options?: {selector?: string *,exact?: boolean true,igno…...

Vcenter esxi web界面访问提示权限被拒绝

一、问题现象 原因 应该是在vCenter中添加主机的时候&#xff0c;将锁定模式设置成了严格。 二、解决过程 2.1 方式一 BMC登录主机&#xff0c;连接显示器和键盘。 输入账号密码&#xff0c;按F2进行设置&#xff0c;将会打开一个界面&#xff0c;第一个选项是设置密码&…...

掌握FilterOutputStream类!

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java IO相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…...

YOLOv8改进 | 图像去雾 | 特征融合注意网络FFA-Net增强YOLOv8对于模糊图片检测能力(北大和北航联合提出)

一、本文介绍 本文给大家带来的改进机制是由北大和北航联合提出的FFA-net: Feature Fusion Attention Network for Single Image Dehazing图像增强去雾网络&#xff0c;该网络的主要思想是利用特征融合注意力网络&#xff08;Feature Fusion Attention Network&#xff09;直接…...

Python (用户登录、身份归属地查询添加异常处理、绘制多角星、电影信息提取)

任务一&#xff1a;用户登录 登录系统通常分为普通用户与管理员权限&#xff0c;在用户登录系统时&#xff0c;可以根据自身权限进行选择登录。本任务要求实现一个用户登录的程序&#xff0c;该程序分为管理员用户与普通用户&#xff0c;其中管理员账号密码在程序中设定&#…...

Set cancelled by MemoryScratchSinkOperator

Bug信息 Caused by: com.starrocks.connector.spark.exception.StarrocksInternalException: StarRocks server StarRocks BE{host=10.9.14.39, port=9060} internal failed, status code [CANCELLED] error message is [Set cancelled by MemoryScratchSinkOperator]Bug产生的…...

Python 查找PDF中的指定文本并高亮显示

在处理大量PDF文档时&#xff0c;有时我们需要快速找到特定的文本信息。本文将提供以下三个Python示例来帮助你在PDF文件中快速查找并高亮指定的文本。 查找并高亮PDF中所有的指定文本查找并高亮PDF某个区域内的指定文本使用正则表达式搜索指定文本并高亮 本文将用到国产第三方…...

岩土工程渗流问题之有限单元法:理论、模块化编程实现、开源程序应用

有限单元法在岩土工程问题中应用非常广泛&#xff0c;很多商业软件如Plaxis/Abaqus/Comsol等都采用有限单元解法。尽管各类商业软件使用方便&#xff0c;但其使用对用户来说往往是一个“黑箱子”。相比而言&#xff0c;开源的有限元程序计算方法透明、计算过程可控&#xff0c;…...

解决 :nvrtc: error: invalid value for --gpu-architecture (-arch)

核心&#xff1a;在显卡安装的cuda版本适配的pytorch中&#xff0c;更换pytorch的版本 刚遇到这个错误时&#xff0c;在网上搜索了一下&#xff0c;感谢博主1和博主2的解决方法带给我的启发。 标题服务器cuda是11.3版本&#xff0c;配置其他环境“御用”的pytorch安装语句 co…...

Rust教程:How to Rust-从开始之前到Hello World

本文为第0篇 专栏简介 本专栏是优质Rust技术专栏&#xff0c;推荐精通一门技术栈的蟹友&#xff0c;不建议基础的同学&#xff08;无基础学Rust也是牛人[手动捂脸]&#xff09; 感谢Rust圣经开源社区的同学&#xff0c;为后来者提供了非常优秀的Rust学习资源 本文使用&…...

浅谈人工智能

☕️各位观众老爷好&#xff0c;路过点个免费的赞再走呗&#xff01;❤️❤️(*•̀ᴗ•́*)و 前言 随着2024年的到来&#xff0c;人工智能领域正迎来前所未有的变革和发展。随着计算能力的增强、大数据的积累以及机器学习算法的进步&#xff0c; AI的定义和本质 人工智能…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...