mirror of https://github.com/alibaba/easyexcel
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
759 lines
28 KiB
759 lines
28 KiB
package com.alibaba.easytools.spring.oss; |
|
|
|
import java.io.File; |
|
import java.io.InputStream; |
|
import java.io.UnsupportedEncodingException; |
|
import java.net.URL; |
|
import java.net.URLEncoder; |
|
import java.nio.charset.StandardCharsets; |
|
import java.util.Calendar; |
|
import java.util.Date; |
|
import java.util.LinkedHashMap; |
|
import java.util.Map; |
|
import java.util.Optional; |
|
import java.util.UUID; |
|
import java.util.concurrent.TimeUnit; |
|
|
|
import com.alibaba.easytools.base.constant.SymbolConstant; |
|
import com.alibaba.easytools.base.excption.BusinessException; |
|
import com.alibaba.easytools.base.excption.CommonErrorEnum; |
|
import com.alibaba.easytools.base.excption.SystemException; |
|
import com.alibaba.easytools.common.util.EasyEnumUtils; |
|
import com.alibaba.easytools.common.util.EasyOptionalUtils; |
|
import com.alibaba.fastjson.JSON; |
|
import com.alibaba.fastjson.JSONArray; |
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.aliyun.oss.OSSClient; |
|
import com.aliyun.oss.OSSErrorCode; |
|
import com.aliyun.oss.OSSException; |
|
import com.aliyun.oss.common.utils.HttpUtil; |
|
import com.aliyun.oss.internal.Mimetypes; |
|
import com.aliyun.oss.internal.OSSUtils; |
|
import com.aliyun.oss.internal.RequestParameters; |
|
import com.aliyun.oss.model.CannedAccessControlList; |
|
import com.aliyun.oss.model.GeneratePresignedUrlRequest; |
|
import com.aliyun.oss.model.GetObjectRequest; |
|
import com.aliyun.oss.model.ObjectMetadata; |
|
import com.aliyuncs.IAcsClient; |
|
import com.aliyuncs.exceptions.ClientException; |
|
import com.aliyuncs.imm.model.v20170906.CreateOfficeConversionTaskRequest; |
|
import lombok.Data; |
|
import lombok.extern.slf4j.Slf4j; |
|
import org.apache.commons.collections4.MapUtils; |
|
import org.apache.commons.lang3.StringUtils; |
|
import org.jsoup.Jsoup; |
|
import org.jsoup.nodes.Document; |
|
import org.jsoup.nodes.Element; |
|
import org.jsoup.select.Elements; |
|
|
|
import static com.aliyun.oss.internal.OSSConstants.DEFAULT_CHARSET_NAME; |
|
|
|
/** |
|
* 自定义OSS的客户端 |
|
* |
|
* @author 是仪 |
|
* @deprecated 使用 EasyOssClient |
|
**/ |
|
@Slf4j |
|
@Data |
|
@Deprecated |
|
public class OssClient { |
|
/** |
|
* https前缀 |
|
*/ |
|
private static final String HTTPS_PREFIX = "https:"; |
|
|
|
/** |
|
* 如果不传kind 全部放到这里 |
|
*/ |
|
private static final String DEFAULT_KIND = "default"; |
|
|
|
/** |
|
* 预览文件的后缀 |
|
*/ |
|
public static final String PREVIEW_SRC_SUFFIX = "_preview"; |
|
/** |
|
* oss枚举的类 |
|
*/ |
|
private Class<? extends OssKindEnum> ossKindEnumClass; |
|
|
|
/** |
|
* 读文件的oss |
|
*/ |
|
private OSSClient readOss; |
|
/** |
|
* 写文件的oss |
|
* 可能会使用 internalEndpoint |
|
*/ |
|
private OSSClient writeOss; |
|
|
|
private String projectName; |
|
private String bucketName; |
|
private String endpoint; |
|
private String baseUrl; |
|
|
|
/** |
|
* bucketName+endpoint |
|
*/ |
|
private String ossDomain; |
|
|
|
/** |
|
* 绑定的域名 类似于 oss.alibaba.com 不要带任何前缀 |
|
* 可以为空 |
|
* 如果为空 则会生成 bucketName+endpoint 的连接 |
|
* 如果不为空 则会生成 domain 的连接 |
|
*/ |
|
private String domain; |
|
/** |
|
* 处理预览的时候有用 |
|
*/ |
|
private IAcsClient iAcsClient; |
|
|
|
/** |
|
* 多媒体项目 |
|
*/ |
|
private String immProject; |
|
|
|
/** |
|
* 是否需要替换域名 |
|
* 生成私有的连接的时候 返回的是 bucketName+endpoint |
|
* 这个时候 我们要把替换成 domain ,当然只有domain!=bucketName+endpoint 的情况 |
|
*/ |
|
private Boolean needReplaceDomain; |
|
|
|
public OssClient(OSSClient oss, String projectName, String bucketName, String endpoint, |
|
Class<? extends OssKindEnum> ossKindEnumClass) { |
|
this(projectName, bucketName, endpoint, ossKindEnumClass, null, null, null); |
|
this.readOss = oss; |
|
this.writeOss = oss; |
|
} |
|
|
|
/** |
|
* 构建oss |
|
* |
|
* @param projectName 项目名 多个项目公用一个oss 可以传值 |
|
* @param bucketName |
|
* @param endpoint |
|
* @param ossKindEnumClass |
|
* @param iAcsClient |
|
* @param immProject |
|
* @param domain |
|
*/ |
|
public OssClient(String projectName, String bucketName, String endpoint, |
|
Class<? extends OssKindEnum> ossKindEnumClass, IAcsClient iAcsClient, String immProject, String domain) { |
|
this.projectName = projectName; |
|
this.iAcsClient = iAcsClient; |
|
this.bucketName = bucketName; |
|
this.endpoint = endpoint; |
|
if (ossKindEnumClass != null) { |
|
this.ossKindEnumClass = ossKindEnumClass; |
|
} else { |
|
this.ossKindEnumClass = DefaultOssKindEnum.class; |
|
} |
|
this.immProject = immProject; |
|
this.ossDomain = bucketName + "." + endpoint; |
|
this.domain = domain; |
|
this.needReplaceDomain = this.domain != null; |
|
// 为空 则拼接domain |
|
if (this.domain == null) { |
|
this.domain = ossDomain; |
|
} |
|
this.baseUrl = "https://" + this.domain + "/"; |
|
} |
|
|
|
/** |
|
* 上传文件 |
|
* |
|
* @param file 文件 不能为空 |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(File file) { |
|
return put(file, null, null, null, null, null); |
|
} |
|
|
|
/** |
|
* 上传文件 |
|
* |
|
* @param file 文件 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(File file, String kind) { |
|
return put(file, kind, null, null, null, null); |
|
} |
|
|
|
/** |
|
* 上传文件 |
|
* |
|
* @param file 文件 不能为空 |
|
* @param ossKindEnum 文件种类 |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(File file, OssKindEnum ossKindEnum) { |
|
return put(file, ossKindEnum.getCode(), null, null, null, ossKindEnum.getObjectAcl()); |
|
} |
|
|
|
/** |
|
* 上传文件 |
|
* |
|
* @param file 文件 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则根据file的名字下载 如果还为空 则自动生成uuid |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(File file, String kind, String fileName) { |
|
return put(file, kind, fileName, null, null, null); |
|
} |
|
|
|
/** |
|
* 上传文件 |
|
* |
|
* @param file 文件 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则根据file的名字下载 如果还为空 则自动生成uuid |
|
* @param contentType 文件类型 可以为空 用户打开时候浏览器的contentType 为空优先根据传入的fileName的后缀区分 如果fileName没有传入则 |
|
* 为空则根据file的名字的后缀区分 如果还为空 则默认流application/octet-stream |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(File file, String kind, String fileName, String contentType) { |
|
return put(file, kind, fileName, contentType, null, null); |
|
} |
|
|
|
/** |
|
* 上传文件 |
|
* |
|
* @param file 文件 不能为空 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则根据file的名字下载 如果还为空 则自动生成uuid |
|
* @param contentType 文件类型 可以为空 用户打开时候浏览器的contentType 为空优先根据传入的fileName的后缀区分 如果fileName没有传入则 |
|
* 为空则根据file的名字的后缀区分 如果还为空 则默认流application/octet-stream |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(String key, File file, String fileName, String contentType) { |
|
return put(file, null, fileName, contentType, key, null); |
|
} |
|
|
|
/** |
|
* 上传文件 |
|
* |
|
* @param file 文件 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则根据file的名字下载 如果还为空 则自动生成uuid |
|
* @param contentType 文件类型 可以为空 用户打开时候浏览器的contentType 为空优先根据传入的fileName的后缀区分 如果fileName没有传入则 |
|
* 为空则根据file的名字的后缀区分 如果还为空 则默认流application/octet-stream |
|
* @param key 服务器的唯一key |
|
* @param objectAcl 对象权限控制 默认私有的 |
|
* @return 返回文件的唯一key 可以获取文件url |
|
* @see CannedAccessControlList |
|
*/ |
|
public String put(File file, String kind, String fileName, String contentType, String key, |
|
CannedAccessControlList objectAcl) { |
|
if (file == null) { |
|
throw new BusinessException("文件不能为空!"); |
|
} |
|
if (!file.isFile()) { |
|
throw new BusinessException("不能上传目录!"); |
|
} |
|
// 构建用来存储在服务器是上面的唯一目录值 |
|
if (StringUtils.isBlank(key)) { |
|
key = buildKey(kind, fileName); |
|
} |
|
// 构建用户下载的时候显示的文件名 |
|
String fileNameDownload = buildFileName(file, fileName); |
|
writeOss.putObject(bucketName, key, file, buildObjectMetadata(fileNameDownload, contentType, objectAcl)); |
|
return key; |
|
} |
|
|
|
/** |
|
* 根据文件流上传文件 |
|
* |
|
* @param inputStream 文件流 不能为空 |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(InputStream inputStream) { |
|
return put(inputStream, null, null, null, null, null); |
|
} |
|
|
|
/** |
|
* 根据文件流上传文件 |
|
* |
|
* @param inputStream 文件流 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(InputStream inputStream, String kind) { |
|
return put(inputStream, kind, null, null, null, null); |
|
} |
|
|
|
/** |
|
* 根据文件流上传文件 |
|
* |
|
* @param inputStream 文件流 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则自动生成uuid |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(InputStream inputStream, String kind, String fileName) { |
|
return put(inputStream, kind, fileName, null, null, null); |
|
} |
|
|
|
/** |
|
* 根据文件流上传文件 |
|
* |
|
* @param inputStream 文件流 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则自动生成uuid |
|
* @param contentType 文件类型 可以为空 用户打开时候浏览器的contentType 为空优先根据传入的fileName的后缀区分 如果fileName没有传入则 |
|
* 默认流application/octet-stream |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(InputStream inputStream, String kind, String fileName, String contentType) { |
|
return put(inputStream, kind, fileName, contentType, null, null); |
|
} |
|
|
|
/** |
|
* 根据文件流上传文件 |
|
* |
|
* @param inputStream 文件流 不能为空 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则自动生成uuid |
|
* @param contentType 文件类型 可以为空 用户打开时候浏览器的contentType 为空优先根据传入的fileName的后缀区分 如果fileName没有传入则 |
|
* 默认流application/octet-stream |
|
* @return 返回文件的唯一key 可以获取文件url |
|
*/ |
|
public String put(String key, InputStream inputStream, String fileName, String contentType) { |
|
return put(inputStream, null, fileName, contentType, key, null); |
|
} |
|
|
|
/** |
|
* 根据文件流上传文件 |
|
* |
|
* @param inputStream 文件流 不能为空 |
|
* @param kind 文件种类 可以为空 在每个项目创建OSSKindConstants 尽量不用重复 为空默认放在default下面 |
|
* @param fileName 用户下载时候的文件名 可以为空 为空则自动生成uuid |
|
* @param contentType 文件类型 可以为空 用户打开时候浏览器的contentType 为空优先根据传入的fileName的后缀区分 如果fileName没有传入则 |
|
* 默认流application/octet-stream |
|
* @param key 生成服务器文件存储的key (空的场景,需要生成) |
|
* @param objectAcl 对象权限控制 默认私有的 |
|
* @return 返回文件的唯一key 可以获取文件url |
|
* @see CannedAccessControlList |
|
*/ |
|
public String put(InputStream inputStream, String kind, String fileName, String contentType, String key, |
|
CannedAccessControlList objectAcl) { |
|
|
|
if (inputStream == null) { |
|
throw new BusinessException("文件流不能为空!"); |
|
} |
|
// 构建用来存储在服务器是上面的唯一目录值 |
|
if (StringUtils.isBlank(key)) { |
|
key = buildKey(kind, fileName); |
|
} |
|
// 构建用户下载的时候显示的文件名 |
|
String fileNameDownload = buildFileName(fileName); |
|
// 上传文件 |
|
writeOss.putObject(bucketName, key, inputStream, buildObjectMetadata(fileNameDownload, contentType, objectAcl)); |
|
return key; |
|
} |
|
|
|
/** |
|
* 根据 key获取文件流 |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @return 返回文件流 如果不存在 则会抛出RuntimeException |
|
*/ |
|
public InputStream get(String key) { |
|
return get(key, null); |
|
} |
|
|
|
/** |
|
* 根据 key获取文件流 |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @param process 格式转换 |
|
* @return 返回文件流 如果不存在 则会抛出RuntimeException |
|
*/ |
|
public InputStream get(String key, String process) { |
|
return getOssObject(key, process).getObjectContent(); |
|
} |
|
|
|
/** |
|
* 根据 key获取OSS文件 如果需要获取文件名之类的请用此方法 |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @return 返回文件流 如果不存在 则会抛出RuntimeException |
|
*/ |
|
public OssObject getOssObject(String key) { |
|
return getOssObject(key, null); |
|
} |
|
|
|
/** |
|
* 根据 key获取OSS文件 如果需要获取文件名之类的请用此方法 |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @param process 格式转换 |
|
* @return 返回文件流 如果不存在 则会抛出RuntimeException |
|
*/ |
|
public OssObject getOssObject(String key, String process) { |
|
|
|
if (StringUtils.isBlank(key)) { |
|
throw new BusinessException("获取文件key 不能为空"); |
|
} |
|
try { |
|
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key); |
|
if (StringUtils.isNotBlank(process)) { |
|
getObjectRequest.setProcess(process); |
|
} |
|
return OssObject.buildWithAliyunOssObject(readOss.getObject(getObjectRequest)); |
|
} catch (OSSException e) { |
|
String errorCode = e.getErrorCode(); |
|
if (OSSErrorCode.NO_SUCH_KEY.equals(errorCode)) { |
|
throw new BusinessException("找不到指定文件"); |
|
} |
|
throw new BusinessException(e.getMessage(), e); |
|
} |
|
} |
|
|
|
/** |
|
* 根据Key 获取url |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @return 一个可以访问的url |
|
*/ |
|
public String getUrl(String key) { |
|
return getUrl(key, null, null); |
|
} |
|
|
|
/** |
|
* 根据Key 获取url |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @param process 格式转换 |
|
* @return 一个可以访问的url |
|
*/ |
|
public String getUrl(String key, String process) { |
|
return getUrl(key, null, null, process); |
|
} |
|
|
|
/** |
|
* 根据Key 获取url |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @param expiration 超时时间 可以为空 共有读的不管传不传 都是永久的 私有的不传默认1小时 |
|
* @param timeUnit 超时时间单位 和超时时间一起使用 |
|
* @return 一个可以访问的url |
|
*/ |
|
public String getUrl(String key, Long expiration, TimeUnit timeUnit) { |
|
return getUrl(key, expiration, timeUnit, null); |
|
} |
|
|
|
/** |
|
* 根据Key 获取url |
|
* |
|
* @param key 文件key 在上传的时候获得 |
|
* @param expiration 超时时间 可以为空 共有读的不管传不传 都是永久的 私有的不传默认1小时 |
|
* @param timeUnit 超时时间单位 和超时时间一起使用 |
|
* @param process 格式转换 |
|
* @return 一个可以访问的url |
|
*/ |
|
public String getUrl(String key, Long expiration, TimeUnit timeUnit, String process) { |
|
if (StringUtils.isBlank(key)) { |
|
return null; |
|
} |
|
String kind = StringUtils.split(key, SymbolConstant.SLASH)[0]; |
|
OssKindEnum kindEnum = EasyEnumUtils.getEnum(ossKindEnumClass, kind); |
|
|
|
// 使用默认的样式处理 |
|
if (process == null) { |
|
process = kindEnum.getProcess(); |
|
} |
|
|
|
// 代表是公有读 直接拼接连接即可 |
|
if (kindEnum != null && kindEnum.getObjectAcl() == CannedAccessControlList.PublicRead) { |
|
Map<String, String> params = new LinkedHashMap<>(); |
|
if (StringUtils.isNotBlank(process)) { |
|
params.put(RequestParameters.SUBRESOURCE_PROCESS, process); |
|
} |
|
if (MapUtils.isEmpty(params)) { |
|
return baseUrl + OSSUtils.determineResourcePath(bucketName, key, |
|
readOss.getClientConfiguration().isSLDEnabled()); |
|
} else { |
|
String queryString = HttpUtil.paramToQueryString(params, DEFAULT_CHARSET_NAME); |
|
return baseUrl + OSSUtils.determineResourcePath(bucketName, key, |
|
readOss.getClientConfiguration().isSLDEnabled()) + "?" + queryString; |
|
} |
|
} |
|
|
|
// 设置默认超时时间 |
|
if (expiration == null) { |
|
expiration = 5L; |
|
timeUnit = TimeUnit.MINUTES; |
|
} else { |
|
if (timeUnit == null) { |
|
throw new BusinessException("超时时间和超时时间单位必须同时为空或者同时不为空"); |
|
} |
|
} |
|
|
|
Date expirationDate = new Date(System.currentTimeMillis() + timeUnit.toMillis(expiration)); |
|
GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, key); |
|
generatePresignedUrlRequest.setExpiration(expirationDate); |
|
if (StringUtils.isNotBlank(process)) { |
|
generatePresignedUrlRequest.setProcess(process); |
|
} |
|
URL url = readOss.generatePresignedUrl(generatePresignedUrlRequest); |
|
String urlString = url.toString(); |
|
if (needReplaceDomain) { |
|
urlString = urlString.replaceFirst(ossDomain, domain); |
|
} |
|
return urlString; |
|
} |
|
|
|
/** |
|
* 创建一个转换office 的任务。过一段时间会自动完成 |
|
* |
|
* @param key |
|
*/ |
|
public void createOfficeConversionTask(String key) { |
|
String srcUri = "oss://" + bucketName + "/" + key; |
|
CreateOfficeConversionTaskRequest createOfficeConversionTaskRequest = new CreateOfficeConversionTaskRequest(); |
|
createOfficeConversionTaskRequest.setProject(immProject); |
|
createOfficeConversionTaskRequest.setSrcUri(srcUri); |
|
createOfficeConversionTaskRequest.setTgtType("vector"); |
|
createOfficeConversionTaskRequest.setTgtUri(srcUri + PREVIEW_SRC_SUFFIX); |
|
try { |
|
iAcsClient.getAcsResponse(createOfficeConversionTaskRequest); |
|
} catch (ClientException e) { |
|
throw new SystemException("创建转换office任务失败.", e); |
|
} |
|
} |
|
|
|
/** |
|
* 根据url生成key |
|
* |
|
* @param url 一个可以访问的url |
|
* @return 唯一key |
|
*/ |
|
public String urlToKey(String url) { |
|
if (StringUtils.isBlank(url)) { |
|
throw new BusinessException("url转key url不能为空"); |
|
} |
|
try { |
|
URL urlObject = new URL(url); |
|
String path = urlObject.getPath(); |
|
return path.substring(1); |
|
} catch (Exception e) { |
|
throw new BusinessException("请传入正确的url"); |
|
} |
|
} |
|
|
|
/** |
|
* 根据key 移除文件 |
|
* |
|
* @param key |
|
*/ |
|
public void delete(String key) { |
|
writeOss.deleteObject(bucketName, key); |
|
} |
|
|
|
/** |
|
* 构建 请求参数 |
|
* |
|
* @param fileNameDownload |
|
* @param contentType |
|
* @return |
|
*/ |
|
private ObjectMetadata buildObjectMetadata(String fileNameDownload, String contentType, |
|
CannedAccessControlList objectAcl) { |
|
if (objectAcl == null) { |
|
objectAcl = CannedAccessControlList.Private; |
|
} |
|
if (objectAcl != CannedAccessControlList.Private && objectAcl != CannedAccessControlList.PublicRead) { |
|
throw new SystemException("目前仅仅支持私有和共有读类型。"); |
|
} |
|
ObjectMetadata objectMetadata = new ObjectMetadata(); |
|
if (StringUtils.isBlank(contentType)) { |
|
contentType = Mimetypes.getInstance().getMimetype(fileNameDownload); |
|
} |
|
objectMetadata.setContentType(contentType); |
|
try { |
|
// 防止中文乱码 |
|
fileNameDownload = URLEncoder.encode(fileNameDownload, StandardCharsets.UTF_8.name()).replaceAll("\\+", |
|
"%20"); |
|
} catch (UnsupportedEncodingException e) { |
|
throw new SystemException(CommonErrorEnum.COMMON_SYSTEM_ERROR, "不支持的字符编码", e); |
|
} |
|
objectMetadata.setContentDisposition("filename*=utf-8''" + fileNameDownload); |
|
// 默认私有 |
|
objectMetadata.setObjectAcl(objectAcl); |
|
return objectMetadata; |
|
} |
|
|
|
/** |
|
* 用来 下载时 显示给用户的名称 |
|
* |
|
* @param fileName |
|
* @return |
|
*/ |
|
private String buildFileName(String fileName) { |
|
if (StringUtils.isNotBlank(fileName)) { |
|
return fileName; |
|
} |
|
return UUID.randomUUID().toString(); |
|
} |
|
|
|
/** |
|
* 用来 下载时 显示给用户的名称 |
|
* |
|
* @param file |
|
* @param fileName |
|
* @return |
|
*/ |
|
private String buildFileName(File file, String fileName) { |
|
if (StringUtils.isNotBlank(fileName)) { |
|
return fileName; |
|
} |
|
fileName = file.getName(); |
|
if (StringUtils.isNotBlank(fileName)) { |
|
return fileName; |
|
} |
|
return UUID.randomUUID().toString(); |
|
} |
|
|
|
/** |
|
* 生成服务器文件存储的key 统一看不到名字 看不到后缀 |
|
* 有些场景需要提前生成唯一存储key |
|
* |
|
* @param kind |
|
* @param fileName |
|
* @return |
|
*/ |
|
public String buildKey(String kind, String fileName) { |
|
if (StringUtils.isBlank(kind)) { |
|
kind = DEFAULT_KIND; |
|
} |
|
StringBuilder key = new StringBuilder(); |
|
if (StringUtils.isNotBlank(projectName)) { |
|
key.append(projectName); |
|
key.append("/"); |
|
} |
|
key.append(kind); |
|
key.append("/"); |
|
Calendar calendar = Calendar.getInstance(); |
|
key.append(calendar.get(Calendar.YEAR)); |
|
key.append("/"); |
|
key.append(StringUtils.leftPad(Integer.toString(calendar.get(Calendar.MONTH) + 1), 2, "0")); |
|
key.append("/"); |
|
key.append(UUID.randomUUID()); |
|
|
|
Optional.ofNullable(fileName).map(fileNameData -> { |
|
int lastDotIndex = fileNameData.lastIndexOf(SymbolConstant.DOT); |
|
if (lastDotIndex >= 0) { |
|
return fileNameData.substring(lastDotIndex); |
|
} |
|
return null; |
|
}).ifPresent(key::append); |
|
return key.toString(); |
|
} |
|
|
|
/** |
|
* 将一个html中需要授权的资源替换成授权完成 |
|
* |
|
* @param html html文本 |
|
* @param ossKindEnum 需要处理的对象的oss 枚举 |
|
* @return 授权完成的html |
|
*/ |
|
public String htmlSourceAuthorization(String html, OssKindEnum ossKindEnum) { |
|
if (StringUtils.isBlank(html)) { |
|
return html; |
|
} |
|
Document doc = Jsoup.parse(html); |
|
// 查找所有image 标签 |
|
Elements imgs = doc.getElementsByTag("img"); |
|
for (Element img : imgs) { |
|
img.attr("src", |
|
getUrlFromUrl(img.attr("src"), EasyOptionalUtils.mapTo(ossKindEnum, OssKindEnum::getProcess))); |
|
} |
|
doc.outputSettings().prettyPrint(false); |
|
return doc.body().html(); |
|
} |
|
|
|
/** |
|
* 将一个json中需要授权的资源替换成授权完成 |
|
* |
|
* 这里注意 这个json 文本很奇怪。类似于:["root",{},["p",{},["span",{"data-type":"text"},["span",{"data-type":"leaf"},"测试"], |
|
* ["span",{"italic":true,"data-type":"leaf"},"文本"]]]] |
|
* |
|
* @param json json文本 |
|
* @param ossKindEnum 需要处理的对象的oss 枚举 |
|
* @return 授权完成的html |
|
*/ |
|
public String jsonSourceAuthorization(String json, OssKindEnum ossKindEnum) { |
|
if (StringUtils.isBlank(json)) { |
|
return json; |
|
} |
|
JSONArray jsonArray = JSON.parseArray(json); |
|
|
|
// 给资源授权 |
|
jsonSourceAuthorization(jsonArray, ossKindEnum); |
|
return jsonArray.toString(); |
|
} |
|
|
|
private void jsonSourceAuthorization(JSONArray jsonArray, OssKindEnum ossKindEnum) { |
|
// 如果小于2级的标签 需要忽略 |
|
if (jsonArray.size() < 2) { |
|
return; |
|
} |
|
// 递归 第三个及其以后的节点 |
|
for (int i = 2; i < jsonArray.size(); i++) { |
|
Object data = jsonArray.get(i); |
|
// 可能是 字符串 也是可能是数组 |
|
// 这里只需要考虑是数组的情况 |
|
if (data instanceof JSONArray) { |
|
jsonSourceAuthorization((JSONArray)data, ossKindEnum); |
|
} |
|
} |
|
|
|
// 处理本节点的数据 |
|
// 第0个数组代表标签名字 |
|
String tagName = jsonArray.getString(0); |
|
//代表不是图片 |
|
if (!"img".equals(tagName)) { |
|
return; |
|
} |
|
// 第1个数组代表 样式 |
|
JSONObject style = jsonArray.getJSONObject(1); |
|
if (style == null) { |
|
return; |
|
} |
|
style.put("src", |
|
getUrlFromUrl(style.getString("src"), EasyOptionalUtils.mapTo(ossKindEnum, OssKindEnum::getProcess))); |
|
} |
|
|
|
/** |
|
* 给一个路径资源授权 |
|
* |
|
* @param url 一个url |
|
* @param process process处理 |
|
* @return |
|
*/ |
|
public String getUrlFromUrl(String url, String process) { |
|
if (StringUtils.isBlank(url)) { |
|
return url; |
|
} |
|
try { |
|
// 有空可能没有http 协议 默认https |
|
if (url.startsWith("//")) { |
|
url = HTTPS_PREFIX + url; |
|
} |
|
URL sourceUrl = new URL(url); |
|
// 代表不是当前 环境的资源 |
|
if (!domain.equals(sourceUrl.getHost()) && !ossDomain.equals(sourceUrl.getHost())) { |
|
return url; |
|
} |
|
return getUrl(sourceUrl.getPath().substring(1), process); |
|
} catch (Exception e) { |
|
log.error("错误的路径:{}", url, e); |
|
return url; |
|
} |
|
} |
|
|
|
} |
|
|
|
|