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.
760 lines
28 KiB
760 lines
28 KiB
10 months ago
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|