1.配置类
@Bean
public OSS ossClient(){
// 创建OSSClient实例。
CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKey, secretKey);
// 创建ClientBuilderConfiguration。
// ClientBuilderConfiguration是OSSClient的配置类,可配置代理、连接超时、最大连接数等参数。
ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
// 设置是否支持CNAME。CNAME用于将自定义域名绑定到目标Bucket。
conf.setSupportCname(true);
// 设置Socket层传输数据的超时时间,默认为50000毫秒。
conf.setSocketTimeout(10000);
// 设置建立连接的超时时间,默认为50000毫秒。
conf.setConnectionTimeout(10000);
// 设置失败请求重试次数,默认为3次。
conf.setMaxErrorRetry(5);
return new OSSClientBuilder().build(endpoint, credentialsProvider, conf);
}
2.接口
public interface FileUploadService {
/**
* 将路径变为共享路径
* @param url
* @return
*/
String fileUrlToShareUrl(String url);
/**
* 文件是否存在
*
* @param url
* @return
*/
boolean fileIsExist(String url);
/**
* @param url
* @return
* @throws HttpStatusException
*/
boolean isTemporaryFile(String url) throws HttpStatusException;
/**
* 上传到临时文件里
*
* @param name
* @param stream
* @return
*/
String uploadTempFile(String name, InputStream stream);
/**
* 移动一个文件
*
* @param oldUrl
* @param newUrl
* @throws HttpStatusException
*/
void moveFile(String oldUrl, String newUrl) throws HttpStatusException;
/**
* 删除一个文件
*
* @param url
* @return
*/
boolean delFile(String url);
/**
* 校验文件名
*
* @param originalFilename
* @return
* @throws HttpStatusException
*/
String validateFileName(String originalFilename) throws HttpStatusException;
/**
* 从url按一定规则取出文件名
*
* @param url
* @return
* @throws CodeException
*/
String getUrlFileName(String url) throws CodeException;
/**
* 将临时文件路径转化为真实文件路径
*
* @param url 文件路径
* @param prefix 文件名前面的路径
* @return
*/
String urlChangeToTruthUrl(String url, String prefix) throws CodeException;
/**
* 上传文件
*
* @param url
* @param stream
*/
void uploadFile(String url, InputStream stream);
/**
* 下载文件
*
* @param fileName
* @param url
* @param response
* @throws IOException
*/
void downLoadFile(String fileName, String url, HttpServletResponse response) throws IOException, CodeException;
/**
* 处理事务提交后的文件 可删除 可添加
* <p>
* value 为null删除
* value 不为null添加
*
* @param fileDtoMap
*/
void handlerFile(Map<String, ? extends FileDto> fileDtoMap);
/**
* 返回 处理完毕的url 要么是和原来一样,要么就是新的修改过后的文件
*
* @param resultUrlMap
* @param filePrefix 文件前缀
* @return
*/
List<FileDto> handlerUpdateFile(Map<String, FileDto> resultUrlMap, List<FileDto> sourceFileList, List<FileDto> newFileList, String filePrefix) throws CodeException;
/**
* 还原文件路径
* @param fileDtoList
* @throws CodeException
*/
void removeFileUrlHost(List<FileDto> fileDtoList) throws CodeException;
}
3.实现
public class OssUploadServiceImpl implements FileUploadService {
private static final String truthDir = "truth/";
private static final String temporaryDir = "temporary/";
private final OSS ossClient;
private static final Logger log = LoggerFactory.getLogger(OssUploadServiceImpl.class);
private final String bucketName;
public OssUploadServiceImpl(OSS ossClient, String bucketName) {
this.ossClient = ossClient;
this.bucketName = bucketName;
}
public void list(String prefix) {
// 列举文件。如果不设置keyPrefix,则列举存储空间下的所有文件。如果设置keyPrefix,则列举包含指定前缀的文件。
ObjectListing objectListing = ossClient.listObjects(bucketName, prefix);
List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
for (OSSObjectSummary s : sums) {
System.out.println("==============");
System.out.println("\t" + s.getKey());
System.out.println(s);
System.out.println(s.getType());
System.out.println(s.getETag());
System.out.println("==============");
}
}
/**
* 把文件路径变为共享路径
* @param url
* @return
*/
@Override
public String fileUrlToShareUrl(String url) {
try {
if (!fileIsExist(url)){
return null;
}
// 指定生成的签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
// 生成签名URL。
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, url, HttpMethod.GET);
// 设置过期时间。
request.setExpiration(expiration);
// 通过HTTP GET请求生成签名URL。
URL signedUrl = ossClient.generatePresignedUrl(request);
return signedUrl.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean fileIsExist(String url) {
try {
if (StringUtils.strIsBlank(url)){
return false;
}
return ossClient.doesObjectExist(bucketName, url);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean isTemporaryFile(String url) throws HttpStatusException {
if (url.startsWith(temporaryDir)) {
return true;
} else if (url.startsWith(truthDir)) {
return false;
} else {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, com.coweixin.heatsource.common.ErrorCode.PARAM_ERROR, "文件路径不合法 url=" + url);
}
}
@Override
public String uploadTempFile(String name, InputStream stream) {
String url = temporaryDir + UUID.randomUUID().toString() + name;
uploadFile(url, stream);
return url;
}
@Override
public void moveFile(String oldUrl, String newUrl) throws HttpStatusException {
if (!fileIsExist(oldUrl)) {
log.error("临时文件不存在 一个文件上传失败: url={} ", oldUrl);
return;
}
// 拷贝文件。
try {
ossClient.copyObject(bucketName, oldUrl, bucketName, newUrl);
delFile(oldUrl);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean delFile(String url) {
// 删除文件或目录。如果要删除目录,目录必须为空。
try {
if (!fileIsExist(url)) {
log.error("文件不存在 一个文件删除失败: url={} ", url);
return false;
}
ossClient.deleteObject(bucketName, url);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public String validateFileName(String originalFilename) throws HttpStatusException {
if (originalFilename == null || originalFilename.contains("/") || originalFilename.contains(File.separator) || originalFilename.startsWith("*") || originalFilename.startsWith("&") || originalFilename.startsWith("?") || originalFilename.startsWith("$")) {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, com.coweixin.heatsource.common.ErrorCode.PARAM_ERROR, "文件名不能为空,且不能以/ * & ? $ 开头");
}
String trimName = originalFilename.trim();
if (!trimName.contains(".")) {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, com.coweixin.heatsource.common.ErrorCode.PARAM_ERROR, "文件名必须包含扩展名");
}
if (trimName.length() <= 3 || trimName.length() >= 30) {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, com.coweixin.heatsource.common.ErrorCode.PARAM_ERROR, "文件名长度不得小于3或超过30");
}
return trimName;
}
@Override
public String getUrlFileName(String url) throws CodeException {
try {
List<String> list = StrSplitter.splitPath(url);
if (!list.isEmpty()) {
String endUrl = list.get(list.size() - 1);
return endUrl.substring(36);
}
throw new RuntimeException();
} catch (Exception e) {
throw new CodeException(com.coweixin.common.exception.ErrorCode.PARAM_FORMAT_ERR, "文件路径不合法 url=" + url);
}
}
@Override
public String urlChangeToTruthUrl(String url, String prefix) throws CodeException {
String urlFileName = getUrlFileName(url);
return truthDir + prefix + UUID.randomUUID() + urlFileName;
}
/**
* 上传文件
*
* @param url
* @param stream
*/
@Override
public void uploadFile(String url, InputStream stream) {
try {
ossClient.putObject(new PutObjectRequest(bucketName, url, stream));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void downLoadFile(String fileName, String url, HttpServletResponse response) throws IOException, CodeException {
// ossObject包含文件所在的存储空间名称、文件名称、文件元数据以及一个输入流。
if (!fileIsExist(url)){
throw new CodeException(ErrorCode.PARAM_FORMAT_ERR,"文件不存在");
}
OSSObject ossObject = ossClient.getObject(bucketName, url);
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
try (InputStream fis = ossObject.getObjectContent()) {
IOUtils.copy(fis, response.getOutputStream());
}
}
@Override
public void handlerFile(Map<String, ? extends FileDto> fileDtoMap) {
for (Map.Entry<String, ? extends FileDto> entry : fileDtoMap.entrySet()) {
try {
String oldUrl = entry.getKey();
FileDto value = entry.getValue();
if (value != null) {
//value不为空为添加
this.moveFile(oldUrl, value.getUrl());
} else {
//否则删除
this.delFile(oldUrl);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public List<FileDto> handlerUpdateFile(Map<String, FileDto> resultUrlMap, List<FileDto> sourceFileList, List<FileDto> newFileList, String filePrefix) throws CodeException {
if (sourceFileList == null || sourceFileList.isEmpty()) {
//原来的是空集或空,那么可以放心大胆的添加操作
for (FileDto fileDto : newFileList) {
String url = fileDto.getUrl();
String newTruthUrl = urlChangeToTruthUrl(url, filePrefix);
fileDto.setUrl(newTruthUrl);
resultUrlMap.put(url, fileDto);
}
} else if (newFileList == null || newFileList.isEmpty()) {
//新来的是空集或空,那么可以放心大胆的删除操作
for (FileDto fileDto : sourceFileList) {
String url = fileDto.getUrl();
resultUrlMap.put(url, null);//
}
} else {
//两个都不为空
for (FileDto fileDto : sourceFileList) {
if (newFileList.contains(fileDto)) {
//新的包含了,就什么都不操作
} else {
//新的不包含就删除
resultUrlMap.put(fileDto.getUrl(), null);
}
}
newFileList.stream()
.filter(fileDto -> !sourceFileList.contains(fileDto))
.forEach(fileDto -> {
try {
String url = fileDto.getUrl();
String newTruthUrl = urlChangeToTruthUrl(url, filePrefix);
fileDto.setUrl(newTruthUrl);
resultUrlMap.put(url, fileDto);
} catch (Exception e) {
e.printStackTrace();
}
});
}
return newFileList;
}
@Override
public void removeFileUrlHost(List<FileDto> fileDtoList) throws CodeException {
if (fileDtoList==null||fileDtoList.isEmpty() ) {
return;
}
for (FileDto fileDto : fileDtoList) {
try {
String url = fileDto.getUrl();
if (StringUtils.strIsBlank(url)){
throw new CodeException(ErrorCode.PARAM_FORMAT_ERR,"路径不能为空");
}
String decodeUrl = URLDecoder.decode(url, StandardCharsets.UTF_8.toString());
String regex = "^https?://[^/]+/(.*?)(\\?[^#]*)?$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(decodeUrl);
if (matcher.find()) {
// 获取匹配的路径部分
url = matcher.group(1);
fileDto.setUrl(url);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
4.本地实现
public class LocalUploadServiceImpl implements FileUploadService {
private static final String rootDir = "C:/environment/nginx-1.26.1/html/static/";
private static final String truthDir = "truth/";
private static final String temporaryDir = "temporary/";
private static final Logger log = LoggerFactory.getLogger(LocalUploadServiceImpl.class);
private static final String serverAddrHost = "http://192.168.202.57/";
@Override
public String fileUrlToShareUrl(String url) {
return serverAddrHost+url;
}
@Override
public boolean fileIsExist(String url) {
return new File(rootDir + url).exists();
}
public File getFile(String url) {
return new File(rootDir + url);
}
@Override
public boolean isTemporaryFile(String url) throws HttpStatusException {
if (url.startsWith(temporaryDir)) {
return true;
} else if (url.startsWith(truthDir)) {
return false;
} else {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, ErrorCode.PARAM_ERROR, "文件路径不合法 url=" + url);
}
}
@Override
public String uploadTempFile(String name, InputStream stream) {
String url = UUID.randomUUID().toString() + name;
FileWriter fileWriter = new FileWriter(rootDir + temporaryDir + url);
fileWriter.writeFromStream(stream);
return temporaryDir + url;
}
@Override
public void moveFile(String oldUrl, String newUrl) throws HttpStatusException {
File src = getFile(oldUrl);
if (!src.exists()) {
log.error("临时文件不存在 一个文件上传失败: url={} ", oldUrl);
return;
}
File target = new File(rootDir + newUrl);
FileUtil.move(src, target, true);
}
@Override
public boolean delFile(String url) {
File file = new File(rootDir + url);
boolean exists = file.exists();
boolean isFile = file.isFile();
if (!exists) {
log.error("删除的文件不存在 url={}", url);
return false;
}
if (!isFile) {
log.error("删除的不是文件 url={}", url);
return false;
}
boolean del = FileUtil.del(file);
return del;
}
@Override
public String validateFileName(String originalFilename) throws HttpStatusException {
if (originalFilename == null || originalFilename.contains("/") || originalFilename.contains(File.separator) || originalFilename.startsWith("*") || originalFilename.startsWith("&") || originalFilename.startsWith("?") || originalFilename.startsWith("$")) {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, ErrorCode.PARAM_ERROR, "文件名不能为空,且不能以/ * & ? $ 开头");
}
String trimName = originalFilename.trim();
if (!trimName.contains(".")) {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, ErrorCode.PARAM_ERROR, "文件名必须包含扩展名");
}
if (trimName.length() <= 3 || trimName.length() >= 30) {
throw new HttpStatusException(HttpStatus.BAD_REQUEST, ErrorCode.PARAM_ERROR, "文件名长度不得小于3或超过30");
}
return trimName;
}
@Override
public String getUrlFileName(String url) throws CodeException {
try {
List<String> list = StrSplitter.splitPath(url);
if (!list.isEmpty()) {
String endUrl = list.get(list.size() - 1);
return endUrl.substring(36);
}
throw new RuntimeException();
} catch (Exception e) {
throw new CodeException(com.coweixin.common.exception.ErrorCode.PARAM_FORMAT_ERR, "文件路径不合法 url=" + url);
}
}
@Override
public String urlChangeToTruthUrl(String url, String prefix) throws CodeException {
String urlFileName = getUrlFileName(url);
return truthDir + prefix + UUID.randomUUID() + urlFileName;
}
@Override
public void uploadFile(String url, InputStream stream) {
FileWriter fileWriter = new FileWriter(rootDir + url);
fileWriter.writeFromStream(stream);
}
@Override
public void downLoadFile(String fileName, String url, HttpServletResponse response) throws IOException {
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
try (FileInputStream fis = new FileInputStream(rootDir + url)) {
IOUtils.copy(fis, response.getOutputStream());
}
}
@Override
public void handlerFile(Map<String,? extends FileDto> fileDtoMap) {
for (Map.Entry<String, ? extends FileDto> entry : fileDtoMap.entrySet()) {
try {
String oldUrl = entry.getKey();
FileDto value = entry.getValue();
if (value != null) {
//value不为空为添加
this.moveFile(oldUrl, value.getUrl());
} else {
//否则删除
this.delFile(oldUrl);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 返回的是可以持久化的 fileDto集合对象
*
* @param resultUrlMap
* @param sourceFileList
* @param newFileList
* @param filePrefix 文件前缀
* @return
* @throws CodeException
*/
@Override
public List<FileDto> handlerUpdateFile(Map<String, FileDto> resultUrlMap, List<FileDto> sourceFileList, List<FileDto> newFileList, String filePrefix) throws CodeException {
if (sourceFileList == null || sourceFileList.isEmpty()) {
//原来的是空集或空,那么可以放心大胆的添加操作
for (FileDto fileDto : newFileList) {
String url = fileDto.getUrl();
String newTruthUrl = urlChangeToTruthUrl(url, filePrefix);
fileDto.setUrl(newTruthUrl);
resultUrlMap.put(url, fileDto);
}
} else if (newFileList == null || newFileList.isEmpty()) {
//新来的是空集或空,那么可以放心大胆的删除操作
for (FileDto fileDto : sourceFileList) {
String url = fileDto.getUrl();
resultUrlMap.put(url, null);//
}
} else {
//两个都不为空
for (FileDto fileDto : sourceFileList) {
if (newFileList.contains(fileDto)) {
//新的包含了,就什么都不操作
} else {
//新的不包含就删除
resultUrlMap.put(fileDto.getUrl(), null);
}
}
newFileList.stream()
.filter(fileDto -> !sourceFileList.contains(fileDto))
.forEach(fileDto -> {
try {
String url = fileDto.getUrl();
String newTruthUrl = urlChangeToTruthUrl(url, filePrefix);
fileDto.setUrl(newTruthUrl);
resultUrlMap.put(url, fileDto);
} catch (Exception e) {
e.printStackTrace();
}
});
}
return newFileList;
}
@Override
public void removeFileUrlHost(List<FileDto> fileDtoList) throws CodeException {
if (fileDtoList==null||fileDtoList.isEmpty() ) {
return;
}
for (FileDto fileDto : fileDtoList) {
String url = fileDto.getUrl();
if (StringUtils.strIsBlank(url)){
throw new CodeException(ErrorCode.PARAM_FORMAT_ERR,"路径不能为空");
}
String regex = "^https?://[^/]+/(.*)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
// 获取匹配的路径部分
url = matcher.group(1);
fileDto.setUrl(url);
}
}
}
}