华润对象存储(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
结合代码中的targetDomainName
、replaceDomainName
可实现外网访问
相关文章:
华润对象存储(OBS)工具类
目录 一、备注二、工具类三、对象存储放在内网,如何实现外网访问 一、备注 1、ObjectBasicInfo、ObjectDetailInfo、ResultBody这三个类可自行替换或者去掉 二、工具类 package com.xxx.util;import com.amazonaws.HttpMethod; import com.amazonaws.auth.AWSStat…...
强缓存和协商缓存的区别?
协商缓存和强缓存是 HTTP 缓存机制中的两种不同的策略,用于减少网络请求并提高网页加载速度。它们之间的主要区别在于缓存的验证方式和服务器返回的响应头。 强缓存: 强缓存是基于过期时间(Expires)和缓存标识(Cache…...

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

openGauss使用BenchmarkSQL进行性能测试(上)
一、前言 本文提供openGauss使用BenchmarkSQL进行性能测试的方法和测试数据报告。 BenchmarkSQL,一个JDBC基准测试工具,内嵌了TPC-C测试脚本,支持很多数据库,如PostgreSQL、Oracle和Mysql等。 TPC-C是专门针对联机交易处理系统…...
Java的线程池机制
Java的线程池机制是用来管理和调度多个线程的工具。通过线程池,可以避免频繁地创建和销毁线程,提高线程的复用率,减少资源消耗。 Java中提供了几种不同类型的线程池: 1、FixedThreadPool(固定大小线程池)…...

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

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

SpringBoot+Vue项目报错(问题已解决)
1、错误日志 2、分析原因: JWT strings must contain exactly 2 period characters. Found: 0 JWT字符串必须包含2个句号字符。发现:0 分析:可以判断出大概可能是token格式出现了问题 3、参考 http://t.csdnimg.cn/hfEiY 4、检查后端代码是否出现问…...
DEAP 自定义交叉操作
在遗传算法中,使用DEAP库来实现自定义的交叉操作可以非常灵活。如果你想模拟多个染色体的情况,通过在染色体的特定区间进行交叉,你需要自定义一个交叉函数。以下是一个示例,展示如何实现一个自定义的交叉函数,该函数允…...
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中添加主机的时候,将锁定模式设置成了严格。 二、解决过程 2.1 方式一 BMC登录主机,连接显示器和键盘。 输入账号密码,按F2进行设置,将会打开一个界面,第一个选项是设置密码&…...

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

YOLOv8改进 | 图像去雾 | 特征融合注意网络FFA-Net增强YOLOv8对于模糊图片检测能力(北大和北航联合提出)
一、本文介绍 本文给大家带来的改进机制是由北大和北航联合提出的FFA-net: Feature Fusion Attention Network for Single Image Dehazing图像增强去雾网络,该网络的主要思想是利用特征融合注意力网络(Feature Fusion Attention Network)直接…...

Python (用户登录、身份归属地查询添加异常处理、绘制多角星、电影信息提取)
任务一:用户登录 登录系统通常分为普通用户与管理员权限,在用户登录系统时,可以根据自身权限进行选择登录。本任务要求实现一个用户登录的程序,该程序分为管理员用户与普通用户,其中管理员账号密码在程序中设定&#…...

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文档时,有时我们需要快速找到特定的文本信息。本文将提供以下三个Python示例来帮助你在PDF文件中快速查找并高亮指定的文本。 查找并高亮PDF中所有的指定文本查找并高亮PDF某个区域内的指定文本使用正则表达式搜索指定文本并高亮 本文将用到国产第三方…...

岩土工程渗流问题之有限单元法:理论、模块化编程实现、开源程序应用
有限单元法在岩土工程问题中应用非常广泛,很多商业软件如Plaxis/Abaqus/Comsol等都采用有限单元解法。尽管各类商业软件使用方便,但其使用对用户来说往往是一个“黑箱子”。相比而言,开源的有限元程序计算方法透明、计算过程可控,…...
解决 :nvrtc: error: invalid value for --gpu-architecture (-arch)
核心:在显卡安装的cuda版本适配的pytorch中,更换pytorch的版本 刚遇到这个错误时,在网上搜索了一下,感谢博主1和博主2的解决方法带给我的启发。 标题服务器cuda是11.3版本,配置其他环境“御用”的pytorch安装语句 co…...

Rust教程:How to Rust-从开始之前到Hello World
本文为第0篇 专栏简介 本专栏是优质Rust技术专栏,推荐精通一门技术栈的蟹友,不建议基础的同学(无基础学Rust也是牛人[手动捂脸]) 感谢Rust圣经开源社区的同学,为后来者提供了非常优秀的Rust学习资源 本文使用&…...

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

vue2中的render函数
<script> export default {components: {},name: "renderElems",render (h, context) {return this.$attrs.vnode;},updated() {} } </script> <style scoped> </style>分析一下上面.vue组件: 组件结构: 这是一个非…...

react实现markdown文件预览
文章目录 react实现markdown文件预览1、实现md文件预览2、解决图片不显示3、实现效果 react实现markdown文件预览 1、实现md文件预览 1️⃣第一步:安装依赖: npm install react-markdown remark-gfmreact-markdown:将 Markdown 渲染为 Rea…...

【大模型:知识图谱】--4.neo4j数据库管理(cypher语法1)
使用neo4j的cypher语法对图数据库进行管理;官网地址:Create, start, and stop databases - Operations Manual 目录 1.neo4j--简介 1.1.Neo4j版本的标准数据库 1.2.默认数据库 1.3.每用户主数据库 1.4.system数据库 2.neo4j--数据库管理 2.1.命名…...
vue3+elementplus表格表头加图标及文字提示
表头加自定义内容有很多种方法,包括使用el-icon,插槽,CSS 伪元素添加图标还有font-awesome等等。 一、方法一:使用render-header属性 <el-table :data"tableData"><el-table-column prop"name" la…...

MATLAB读取文件内容:Excel、CSV和TXT文件解析
MATLAB读取文件内容:Excel、CSV和TXT文件解析 MATLAB 是一款强大的数学与工程计算工具,广泛应用于数据分析、模型构建和图像处理等领域。在处理实际问题时,我们常常需要从文件中读取数据进行分析。本文将介绍如何使用 MATLAB 读取常见的文件…...
VUE混合开发用哪个PHP框架好?
在 Vue.js 主导前端的混合开发浪潮中,一个强大、灵活的后端 API 提供者至关重要。PHP 作为经久不衰的服务器端语言,拥有众多优秀框架。但哪个才是 Vue 混合开发的黄金搭档?本文将深入分析主流 PHP 框架的优劣,帮你找到最适合的那个…...

2025东南亚跨境选择:Lazada VS. Shopee深度对比
东南亚电商市场持续爆发,2025年预计规模突破2000亿美元。对跨境卖家而言,Lazada与Shopee仍是两大核心战场,但平台生态与竞争格局已悄然变化。深入对比,方能制胜未来。 一、平台基因与核心优势对比 维度 Lazada (阿里系) Shopee …...

74. 搜索二维矩阵 (力扣)
给你一个满足下述两条属性的 m x n 整数矩阵: 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。…...

h5的aliplayer-min.js 加密视频会走到debugger
h5的aliplayer-min.js 如果 https://g.alicdn.com/apsara-media-box/imp-web-player/2.19.0/aliplayer-min.js走加密视频的话会有debugger 更换aliplayer-min.js版本解决了 https://g.alicdn.com/apsara-media-box/imp-web-player/2.25.1/aliplayer-min.js 对应css:…...
【PCB工艺】绘制原理图 + PCB设计大纲:最小核心板STM32F103ZET6
绘制原理图和PCB布线之间的联系,在绘制原理图的时候,考虑到后续的PCB设计+嵌入式软件代码的业务逻辑,需要在绘制原理图之初涉及到 硬件设计流程的前期规划。在嵌入式系统开发中,原理图设计是整个项目的基础,直接影响到后续的: PCB 布线效率和质量 ☆☆☆重点嵌入式软件的…...