From 9b85c51b51de74e3a644fb25ca418fecabfb99d0 Mon Sep 17 00:00:00 2001 From: wr <1754607820@qq.com> Date: Mon, 12 Jan 2026 14:47:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=98=BF=E9=87=8C?= =?UTF-8?q?=E4=BA=91oss?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 65 ++++ .../njcn/ali/oss/config/AliYunOssConfig.java | 42 +++ .../com/njcn/ali/oss/util/AliYunOssUtils.java | 299 ++++++++++++++++++ src/main/resources/META-INF/spring.factories | 5 + 4 files changed, 411 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/njcn/ali/oss/config/AliYunOssConfig.java create mode 100644 src/main/java/com/njcn/ali/oss/util/AliYunOssUtils.java create mode 100644 src/main/resources/META-INF/spring.factories diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..53be5a1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + com.njcn + aliyun-oss-springboot-starter + 1.0.0 + + + + nexus-releases + Nexus Release Repository + http://192.168.1.22:8001/nexus/content/repositories/releases/ + + + nexus-snapshots + Nexus Snapshot Repository + http://192.168.1.22:8001/nexus/content/repositories/snapshots/ + + + + 8 + 8 + UTF-8 + 0.5.3 + 4.8.1 + 8.2.1 + + 灿能阿里云oss组件提取的starter模块 + jar + + + + + com.njcn + common-core + 1.0.0 + compile + + + + + com.aliyun.oss + aliyun-sdk-oss + 3.18.0 + + + + + org.slf4j + slf4j-api + 1.7.36 + + + + org.springframework.boot + spring-boot-configuration-processor + true + 2.3.12.RELEASE + compile + + + \ No newline at end of file diff --git a/src/main/java/com/njcn/ali/oss/config/AliYunOssConfig.java b/src/main/java/com/njcn/ali/oss/config/AliYunOssConfig.java new file mode 100644 index 0000000..891c2d3 --- /dev/null +++ b/src/main/java/com/njcn/ali/oss/config/AliYunOssConfig.java @@ -0,0 +1,42 @@ +package com.njcn.ali.oss.config; + +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +/** + * OSS配置类,存储连接所需的核心参数 + * @author web2023 + */ +@Configuration +@ConfigurationProperties( + prefix = "aliyun.io" +) +@Data +public class AliYunOssConfig { + + private String endpoint; + private String accessKey; + private String secretKey; + private String bucket; + + @Bean(destroyMethod = "shutdown") + public OSS getAliYunClient() { + // 1. 校验配置(避免因配置为空导致Bean创建失败) + if (!StringUtils.hasText(endpoint) || !StringUtils.hasText(accessKey) || !StringUtils.hasText(secretKey)) { + throw new IllegalArgumentException("OSS配置缺失:endpoint/accessKey/secretKey不能为空"); + } + return new OSSClientBuilder().build( + this.getEndpoint(), + this.getAccessKey(), + this.getSecretKey() + ); + } + + + +} \ No newline at end of file diff --git a/src/main/java/com/njcn/ali/oss/util/AliYunOssUtils.java b/src/main/java/com/njcn/ali/oss/util/AliYunOssUtils.java new file mode 100644 index 0000000..dd8a6a2 --- /dev/null +++ b/src/main/java/com/njcn/ali/oss/util/AliYunOssUtils.java @@ -0,0 +1,299 @@ +package com.njcn.ali.oss.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ObjUtil; +import com.aliyun.oss.OSS; +import com.aliyun.oss.common.comm.ResponseMessage; +import com.aliyun.oss.model.*; +import com.njcn.ali.oss.config.AliYunOssConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.*; +import java.net.URL; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +/** + * 阿里云OSS核心工具类 + * 封装常用操作:上传、下载、删除、查询、生成临时访问URL等 + * + * @author web2023 + */ +@Component +public class AliYunOssUtils { + private static final Logger logger = LoggerFactory.getLogger(AliYunOssUtils.class); + @Resource + private OSS ossClient; + @Resource + private AliYunOssConfig ossConfig; + + /** + * 上传文件(字节数组) + * + * @param objectName 文件在OSS中的路径/名称 + * @param content 文件字节数组 + * @return 上传结果ETag + */ + public String uploadFile(String objectName, byte[] content) { + return uploadFile(objectName, new ByteArrayInputStream(content)); + } + + /** + * 上传文件(InputStream) + * + * @param objectName 文件在OSS中的路径/名称 + * @param inputStream 文件输入流 + * @return 上传结果ETag + */ + public String uploadFile(String objectName, InputStream inputStream) { + try { + PutObjectResult result = ossClient.putObject( + ossConfig.getBucket(), + objectName, + inputStream + ); + return result.getETag(); + } catch (Exception e) { + logger.error("文件{}上传失败", objectName, e); + throw new RuntimeException("文件上传失败:" + objectName, e); + } + } + + /** + * 上传文件(MultipartFile) + * + * @param objectName 文件在OSS中的路径/名称 + * @param multipartFile 文件输入流 + * @return 上传结果ETag + */ + public String uploadFile(String objectName, MultipartFile multipartFile) { + try { + PutObjectResult result = ossClient.putObject( + ossConfig.getBucket(), + objectName, + multipartFile.getInputStream() + ); + return result.getETag(); + } catch (Exception e) { + logger.error("文件{}上传失败", objectName, e); + throw new RuntimeException("文件上传失败:" + objectName, e); + } + } + + + /** + * 上传本地文件 + * + * @param objectName 文件在OSS中的路径/名称 + * @param localFilePath 本地文件路径 + * @return 上传结果ETag + */ + public String uploadLocalFile(String objectName, String localFilePath) { + File file = new File(localFilePath); + if (!file.exists()) { + throw new RuntimeException("本地文件不存在:" + localFilePath); + } + try { + PutObjectResult result = ossClient.putObject( + ossConfig.getBucket(), + objectName, + file + ); + return result.getETag(); + } catch (Exception e) { + logger.error("本地文件{}上传失败", localFilePath, e); + throw new RuntimeException("本地文件上传失败:" + localFilePath, e); + } + } + + /** + * 下载文件到本地 + * + * @param objectName OSS中的文件名称/路径 + * @param localFilePath 本地保存路径 + */ + public void downloadFile(String objectName, String localFilePath) { + try { + ossClient.getObject( + new GetObjectRequest(ossConfig.getBucket(), objectName), + new File(localFilePath) + ); + } catch (Exception e) { + logger.error("文件{}下载失败", objectName, e); + throw new RuntimeException("文件下载失败:" + objectName, e); + } + } + + /** + * 下载文件为字节流 + * + * @param objectName OSS中的文件名称/路径 + * @return 文件字节流 + */ + public InputStream downloadStream(String objectName) { + try (OSSObject ossObject = ossClient.getObject(ossConfig.getBucket(), objectName); + InputStream inputStream = ossObject.getObjectContent()) { + return inputStream; + } catch (Exception e) { + logger.error("文件{}下载为字节数组失败", objectName, e); + throw new RuntimeException("文件下载为字节数组失败:" + objectName, e); + } + } + + /** + * 下载文件为字节数组 + * + * @param objectName OSS中的文件名称/路径 + * @return 文件字节数组 + */ + public byte[] downloadFileToBytes(String objectName) { + try (OSSObject ossObject = ossClient.getObject(ossConfig.getBucket(), objectName); + InputStream inputStream = ossObject.getObjectContent(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, len); + } + return outputStream.toByteArray(); + } catch (Exception e) { + logger.error("文件{}下载为字节数组失败", objectName, e); + throw new RuntimeException("文件下载为字节数组失败:" + objectName, e); + } + } + + /** + * 下载文件为文件 + * + * @param folderPath OSS中的文件名称/路径 + * @return 文件 + */ + public File getLastFile(String folderPath) { + try { + // 列出文件夹下所有文件(分页处理,避免文件过多) + ObjectListing objectListing = ossClient.listObjects(ossConfig.getBucket(), folderPath); + List objectSummaries = objectListing.getObjectSummaries(); + + // 处理空文件夹 + if (objectSummaries.isEmpty()) { + logger.warn("OSS文件夹 {} 下无文件", folderPath); + return null; + } + + // 按最后修改时间降序排序,取第一个(最新) + String latestFileName = objectSummaries.stream() + .filter(summary -> !summary.getKey().equals(folderPath)) + .max(Comparator.comparing(OSSObjectSummary::getLastModified)) + .map(OSSObjectSummary::getKey) + .orElse(null); + if (ObjUtil.isNull(latestFileName)) { + throw new RuntimeException("OSS文件夹 " + folderPath + " 下无可用文件"); + } + File file = File.createTempFile(latestFileName.split(".xlsx")[0], ".xlsx"); + InputStream inputStream = ossClient.getObject(ossConfig.getBucket(), latestFileName).getObjectContent(); + FileUtil.writeFromStream(inputStream, file); + inputStream.close(); + return file; + } catch (Exception e) { + logger.error("查找OSS文件夹 {} 最新文件失败", folderPath, e); + throw new RuntimeException("查找最新文件失败:" + folderPath, e); + }finally { + + } + } + + public static void main(String[] args) { + String latestFileName="latestFileNam.xlsx"; + + String s = latestFileName.split(".xlsx")[0]; + System.out.println(); + } + /** + * 删除单个文件 + * + * @param objectName OSS中的文件名称/路径 + */ + public void deleteFile(String objectName) { + try { + VoidResult voidResult = ossClient.deleteObject(ossConfig.getBucket(), objectName); + ResponseMessage response = voidResult.getResponse(); + System.out.println(response); + } catch (Exception e) { + logger.error("文件{}删除失败", objectName, e); + throw new RuntimeException("文件删除失败:" + objectName, e); + } + } + + /** + * 批量删除文件 + * + * @param objectNames 文件名称列表 + */ + public void deleteFiles(List objectNames) { + try { + // 静默删除,不返回删除结果 + DeleteObjectsRequest request = new DeleteObjectsRequest(ossConfig.getBucket()) + .withKeys(objectNames); + ossClient.deleteObjects(request); + } catch (Exception e) { + logger.error("批量删除文件失败", e); + throw new RuntimeException("批量删除文件失败", e); + } + } + + /** + * 判断文件是否存在 + * + * @param objectName OSS中的文件名称/路径 + * @return true-存在,false-不存在 + */ + public boolean isFileExist(String objectName) { + try { + boolean exists = ossClient.doesObjectExist(ossConfig.getBucket(), objectName); + return exists; + } catch (Exception e) { + logger.error("判断文件{}是否存在失败", objectName, e); + throw new RuntimeException("判断文件是否存在失败:" + objectName, e); + } + } + + /** + * 生成文件临时访问URL + * + * @param objectName OSS中的文件名称/路径 + * @param expireSeconds 过期时间(秒) + * @return 临时访问URL + */ + public String generatePresignedUrl(String objectName, int expireSeconds) { + try { + Date expiration = new Date(System.currentTimeMillis() + expireSeconds * 1000L); + URL url = ossClient.generatePresignedUrl( + ossConfig.getBucket(), + objectName, + expiration + ); + String urlStr = url.toString(); + logger.info("文件{}生成临时访问URL成功,过期时间:{}秒", objectName, expireSeconds); + return urlStr; + } catch (Exception e) { + logger.error("生成文件{}临时访问URL失败", objectName, e); + throw new RuntimeException("生成临时访问URL失败:" + objectName, e); + } + } + + /** + * 关闭OSS客户端 + */ + public void close() { + if (ossClient != null) { + ossClient.shutdown(); + logger.info("OSS客户端已关闭"); + } + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..a7a06ba --- /dev/null +++ b/src/main/resources/META-INF/spring.factories @@ -0,0 +1,5 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.njcn.ali.oss.config.AliYunOssConfig,\ + com.njcn.ali.oss.util.AliYunOssUtils + +