阿里云OSS对象存储功能的实现步骤 + MinIO本地存储的实现步骤 + 配置文件的编写
一、阿里云OSS对象存储功能的实现步骤
首先 -> 注册阿里云 -> 开通OSS对象存储服务 -> 创建Bucket -> 创建AccessKey
1. 获取四个参数编写到yml文件 参考配置文件1.
aliyun:
oss:
endpoint: https://oss-cn-hangzhou.aliyuncs.com
access-key-id: LTAI5tS9qa9H2uftNY
access-key-secret: VNEIKZfxBpa4HO9YLDx2i
bucket-name: big-event993
2. 编写配置类
AliyunOSSConfig
package com.zxd.AliOSS;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @Auther: Zxd
* @Date: 2024/07/12 20:58
* @Description:
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
3. 编写OSS工具类
AliOSSUtil
package com.zxd.AliOSS;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
/**
* @Auther: Zxd
* @Date: 2024/07/12 14:47
* @Description: 阿里云oss工具类
*/
@Component
public class AliOSSUtil {
@Autowired
private AliyunOSSConfig aliyunOSSConfig;
/**
* 上传文件到OSS(Object Storage Service)。
*
* @param originalFilename 原始文件名,包含文件扩展名。
* @param in 文件的输入流。
* @return 上传后的文件URL。
* @throws Exception 如果上传过程中发生错误。
*/
public String uploadFile(String originalFilename, InputStream in) throws Exception {
// 从原始文件名中提取文件扩展名
String fileExtension = getFileExtension(originalFilename);
// 根据文件扩展名决定文件存储的目录
String directory = getDirectoryByExtension(fileExtension);
// 生成唯一ID,用于文件名的一部分(在文件名后面-四位随机字符),以避免文件名冲突
String uniqueId = UUID.randomUUID().toString().substring(0, 4); // 取UUID的前4个字符
// 构造新文件名,原始文件名加上唯一ID和原始文件扩展名
String newFileName = originalFilename.substring(0, originalFilename.lastIndexOf('.')) + "-" + uniqueId + originalFilename.substring(originalFilename.lastIndexOf('.'));
// 构造新的objectName,添加目录
String newObjectName = directory + "/" + newFileName;
// 创建OSS客户端实例
OSS ossClient = new OSSClientBuilder().build(aliyunOSSConfig.getEndpoint(), aliyunOSSConfig.getAccessKeyId(), aliyunOSSConfig.getAccessKeySecret());
String url = "";
try {
// 创建PutObjectRequest对象,准备上传文件
PutObjectRequest putObjectRequest = new PutObjectRequest(aliyunOSSConfig.getBucketName(), newObjectName, in);
// 执行文件上传操作
PutObjectResult result = ossClient.putObject(putObjectRequest);
// URL编码处理
// 只对文件名部分进行编码
String encodedFileName = URLEncoder.encode(newFileName, StandardCharsets.UTF_8.toString()).replace("+", "%20");
// 不对路径中的斜杠进行编码
String encodedObjectName = newObjectName.substring(0, newObjectName.lastIndexOf('/') + 1) + encodedFileName;
// 构造上传后文件的URL url组成: https://bucket名称.区域节点/objectName
url = "https://" + aliyunOSSConfig.getBucketName() + "." + aliyunOSSConfig.getEndpoint().substring(aliyunOSSConfig.getEndpoint().lastIndexOf("/") + 1) + "/" + encodedObjectName;
} catch (OSSException oe) {
// 处理OSS异常,打印错误信息
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
// 处理客户端异常,打印错误信息
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
// 关闭OSS客户端,释放资源
if (ossClient != null) {
ossClient.shutdown();
}
}
// 返回上传后的文件URL
return url;
}
/**
* 获取文件名的扩展名。
*
* 该方法通过查找字符串中最后一个'.'的位置来确定文件的扩展名。如果找到了'.',则返回'.'后面的部分,
* 并将其转换为小写;如果未找到'.',则返回空字符串。
*
* @param objectName 待处理的文件名或路径名字符串。
* @return 文件名的扩展名,以小写形式表示,如果不存在扩展名则返回空字符串。
*/
private String getFileExtension(String objectName) {
int dotIndex = objectName.lastIndexOf('.');
return dotIndex != -1 ? objectName.substring(dotIndex + 1).toLowerCase() : "";
}
/**
* 根据文件扩展名获取文件目录类别。
*
* 此方法通过文件的扩展名来确定文件的类型,并返回相应的目录类别字符串。
* 支持的文件类型包括音频、视频、图片、文档、压缩文件、代码、数据库、配置、设计和其他类型。
*
* @param extension 文件的扩展名(不包含点号)
* @return 对应的目录类别字符串,如"audio"、"video"、"image"等。
*/
private String getDirectoryByExtension(String extension) {
// 根据文件扩展名确定文件类型并返回相应的目录类别
switch (extension) {
// 音频文件类型
case "mp3":
case "wav":
case "aac":
case "ogg":
case "flac":
case "m4a":
case "wma":
return "audio";
// 视频文件类型
case "mp4":
case "avi":
case "mov":
case "mkv":
case "flv":
case "wmv":
case "webm":
case "mpg":
case "mpeg":
case "rmvb":
return "video";
// 图片文件类型
case "png":
case "jpg":
case "jpeg":
case "gif":
case "bmp":
case "tiff":
case "ico":
case "svg":
return "image";
// 文档文件类型
case "doc":
case "docx":
case "ppt":
case "pptx":
case "xls":
case "xlsx":
case "pdf":
case "txt":
case "md":
case "rtf":
return "word";
// 压缩文件类型
case "zip":
case "rar":
case "gz":
case "tar":
return "zip";
// 代码文件类型
case "java":
case "py":
case "js":
case "html":
case "htm":
case "css":
case "cpp":
case "h":
case "cs":
return "code";
// 数据库文件类型
case "sqlite":
case "db":
case "sql":
return "databases";
// 配置文件类型
case "ini":
case "config":
case "yaml":
case "json":
case "log":
case "sh":
case "bat":
return "configurations";
// 设计文件类型
case "psd":
case "ai":
case "sketch":
return "designs";
// 默认情况,对于未识别的扩展名
default:
return "others";
}
}
/**
* 通过URL删除存储桶内的数据。
*
* @param fileUrl 文件的URL地址。
* @throws Exception 如果删除过程中发生错误。
*/
public void deleteFileByUrl(String fileUrl) throws Exception {
// URL解码
String decodedUrl = URLDecoder.decode(fileUrl, StandardCharsets.UTF_8.toString());
// 提取bucket名称和对象名称
String bucketName = aliyunOSSConfig.getBucketName();
String endpoint = aliyunOSSConfig.getEndpoint();
String baseUrl = "https://" + bucketName + "." + endpoint.substring(endpoint.lastIndexOf("/") + 1);
// 计算objectName
String objectName = decodedUrl.replace(baseUrl + "/", "");
// 创建OSS客户端实例
OSS ossClient = new OSSClientBuilder().build(aliyunOSSConfig.getEndpoint(), aliyunOSSConfig.getAccessKeyId(), aliyunOSSConfig.getAccessKeySecret());
try {
// 执行文件删除操作
ossClient.deleteObject(bucketName, objectName);
} catch (OSSException oe) {
// 处理OSS异常,打印错误信息
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
// 处理客户端异常,打印错误信息
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
// 关闭OSS客户端,释放资源
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
4. 图片上传controller层示例
FileUploadOSSController
package com.zxd.AliOSS;
import com.zxd.pojo.ApiResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* @Auther: Zxd
* @Date: 2024/07/12 10:15
* @Description:
*/
@RestController
@Tag(name = "文件上传")
public class FileUploadOSSController {
@Autowired
private AliOSSUtil aliOSSUtil;
@PostMapping("/uploadOSS")
@Operation(summary = "OSS文件上传")
public ApiResult<String> upload(@RequestParam("file") MultipartFile file) throws Exception {
// 把文件内容存储到阿里云OSS上
// 获取文件名
String originalFilename = file.getOriginalFilename();
// 调用工具类(文件名,文件数据流)上传到阿里云并返回url地址
String url = aliOSSUtil.uploadFile(originalFilename, file.getInputStream());
return ApiResult.success(url);
}
@PostMapping("/deleteOSS")
@Operation(summary = "OSS文件删除")
public ApiResult<String> delete(@RequestParam("fileUrl") String fileUrl) {
try {
// 调用工具类删除文件
aliOSSUtil.deleteFileByUrl(fileUrl);
return ApiResult.success("文件删除成功");
} catch (Exception e) {
// 处理异常并返回失败结果
return ApiResult.error("文件删除失败: " + e.getMessage());
}
}
}
参考链接:https://www.bilibili.com/video/BV14z4y1N7pg?p=36&vd_source=714b8a2323b700f15d0b7b31226f1d5d
二、MinIO存储的实现步骤
1. MinIO在Linux的安装步骤(通过Docker命令安装到容器中)
docker启动minio命令:
docker run -p 9000:9000 -p 9001:9001 --name minio \
-v /mydata/minio/data:/data \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
--restart always \
-d minio/minio server /data --console-address ":9001"
命令解释
docker run -p 9000:9000 -p 9001:9001 --name minio \ 启动命令,映射9000和9001端口,命名为minio
-v /mydata/minio/data:/data \ 挂载数据到/mydata/minio/data
-e MINIO_ROOT_USER=minioadmin \ 设置用户名为minioadmin
-e MINIO_ROOT_PASSWORD=minioadmin \ 设置密码为minioadmin
--restart always \ 设置开机自动启动容器
-d minio/minio server /data --console-address ":9001" 后台启动,
2. 登录MinIO的控制页面 地址为 虚拟机/服务器ip:9001 示例:192.168.115.136:9001
输入上面定义的用户名和密码,此处为 minioadmin
3. 创建存储桶
4. 将存储桶权限设置为公开(默认权限为私密)
5. 获取API访问凭证
6. 在application.yml中编写配置文件 注意参数一一对应且有效
#minio配置
minio:
url: http://192.168.115.136:9000 #访问地址,对象存储服务的URL
access-key: HLKXF1QG9TMM3LDOCY5P #Access key账户 写账号也可以
secret-key: x+u8W90DaRFT1qOpcsqlv7pmVgONfXl9oGylYko8 #Secret key密码
bucket-name: test-bucket # 桶名称
# expire: 7200 # 过期时间
7. 编写配置类
MinIOConfig
package com.zxd.MinIO;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @Auther: Zxd
* @Date: 2024/07/12 20:58
* @Description:
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinIOConfig {
private String url;
private String accessKey;
private String secretKey;
private String bucketName;
}
8. 编写Oss工具类
MinIOUtil
package com.zxd.MinIO;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.errors.MinioException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
/**
* @Author: Zxd
* @Date: 2024/07/12 14:47
* @Description: MinIO工具类
*/
@Component
public class MinIOUtil {
@Autowired
private MinIOConfig minioConfig;
/**
* 上传文件到MinIO。
*
* @param originalFilename 原始文件名,包含文件扩展名。
* @param in 文件的输入流。
* @return 上传后的文件URL。
* @throws Exception 如果上传过程中发生错误。
* @description 存入时自动在文件名后面加四位字符,避免文件名重复并且可以根据不同文件的后缀放入不同的文件夹下,具体可以修改工具类中getDirectoryByExtension方法
*/
public String uploadFile(String originalFilename, InputStream in) throws Exception {
// 从原始文件名中提取文件扩展名
String fileExtension = getFileExtension(originalFilename);
// 根据文件扩展名决定文件存储的目录
String directory = getDirectoryByExtension(fileExtension);
// 生成唯一ID,用于文件名的一部分(在文件名后面-四位随机字符),以避免文件名冲突
String uniqueId = UUID.randomUUID().toString().substring(0, 4); // 取UUID的前4个字符
// 构造新文件名,原始文件名加上唯一ID和原始文件扩展名
String newFileName = originalFilename.substring(0, originalFilename.lastIndexOf('.')) + "-" + uniqueId + originalFilename.substring(originalFilename.lastIndexOf('.'));
// 构造新的objectName,添加目录
String newObjectName = directory + "/" + newFileName;
// 创建MinIO客户端实例
MinioClient minioClient = MinioClient.builder()
.endpoint(minioConfig.getUrl())
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
.build();
String url = "";
try {
// 准备上传文件
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(newObjectName)
.stream(in, in.available(), -1)
.build();
// 执行文件上传操作
minioClient.putObject(putObjectArgs);
// URL编码处理
// 只对文件名部分进行编码
String encodedFileName = URLEncoder.encode(newFileName, StandardCharsets.UTF_8.toString()).replace("+", "%20");
// 不对路径中的斜杠进行编码
String encodedObjectName = newObjectName.substring(0, newObjectName.lastIndexOf('/') + 1) + encodedFileName;
// 构造上传后文件的URL
url = minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + encodedObjectName;
} catch (MinioException e) {
// 处理MinIO异常,打印错误信息
System.out.println("Caught an MinioException, which means your request made it to MinIO, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + e.getMessage());
} finally {
// 关闭输入流,释放资源
if (in != null) {
in.close();
}
}
// 返回上传后的文件URL
return url;
}
/**
* 获取文件名的扩展名。
*
* 该方法通过查找字符串中最后一个'.'的位置来确定文件的扩展名。如果找到了'.',则返回'.'后面的部分,
* 并将其转换为小写;如果未找到'.',则返回空字符串。
*
* @param objectName 待处理的文件名或路径名字符串。
* @return 文件名的扩展名,以小写形式表示,如果不存在扩展名则返回空字符串。
*/
private String getFileExtension(String objectName) {
int dotIndex = objectName.lastIndexOf('.');
return dotIndex != -1 ? objectName.substring(dotIndex + 1).toLowerCase() : "";
}
/**
* 根据文件扩展名获取文件目录类别。
*
* 此方法通过文件的扩展名来确定文件的类型,并返回相应的目录类别字符串。
* 支持的文件类型包括音频、视频、图片、文档、压缩文件、代码、数据库、配置、设计和其他类型。
*
* @param extension 文件的扩展名(不包含点号)
* @return 对应的目录类别字符串,如"audio"、"video"、"image"等。
*/
private String getDirectoryByExtension(String extension) {
// 根据文件扩展名确定文件类型并返回相应的目录类别
switch (extension) {
// 音频文件类型
case "mp3":
case "wav":
case "aac":
case "ogg":
case "flac":
case "m4a":
case "wma":
return "audio";
// 视频文件类型
case "mp4":
case "avi":
case "mov":
case "mkv":
case "flv":
case "wmv":
case "webm":
case "mpg":
case "mpeg":
case "rmvb":
return "video";
// 图片文件类型
case "png":
case "jpg":
case "jpeg":
case "gif":
case "bmp":
case "tiff":
case "ico":
case "svg":
return "image";
// 文档文件类型
case "doc":
case "docx":
case "ppt":
case "pptx":
case "xls":
case "xlsx":
case "pdf":
case "txt":
case "md":
case "rtf":
return "word";
// 压缩文件类型
case "zip":
case "rar":
case "gz":
case "tar":
return "zip";
// 代码文件类型
case "java":
case "py":
case "js":
case "html":
case "htm":
case "css":
case "cpp":
case "h":
case "cs":
return "code";
// 数据库文件类型
case "sqlite":
case "db":
case "sql":
return "databases";
// 配置文件类型
case "ini":
case "config":
case "yaml":
case "json":
case "log":
case "sh":
case "bat":
return "configurations";
// 设计文件类型
case "psd":
case "ai":
case "sketch":
return "designs";
// 默认情况,对于未识别的扩展名
default:
return "others";
}
}
/**
* 通过URL删除存储桶内的数据。
*
* @param fileUrl 文件的URL地址。
* @throws Exception 如果删除过程中发生错误。
*/
public void deleteFileByUrl(String fileUrl) throws Exception {
// URL解码
String decodedUrl = URLDecoder.decode(fileUrl, StandardCharsets.UTF_8.toString());
// 提取bucket名称和对象名称
String bucketName = minioConfig.getBucketName();
String objectName = decodedUrl.substring(decodedUrl.indexOf(bucketName) + bucketName.length() + 1);
// 创建MinIO客户端实例
MinioClient minioClient = MinioClient.builder()
.endpoint(minioConfig.getUrl())
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
.build();
try {
// 执行文件删除操作
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
minioClient.removeObject(removeObjectArgs);
} catch (MinioException e) {
// 处理MinIO异常,打印错误信息
System.out.println("Caught an MinioException, which means your request made it to MinIO, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + e.getMessage());
}
}
}
9. 图片上传controller层示例
FileUploadMinIOController
package com.zxd.MinIO;
import com.zxd.pojo.ApiResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* @Auther: Zxd
* @Date: 2024/07/12 10:15
* @Description:
*/
@RestController
@Tag(name = "文件上传")
public class FileUploadMinIOController {
@Autowired
private MinIOUtil minIOUtil;
@PostMapping("/uploadMinIO")
@Operation(summary = "MinIO文件上传")
public ApiResult<String> upload(@RequestParam("file") MultipartFile file) throws Exception {
// 把文件内容存储到阿里云OSS上
// 获取文件名
String originalFilename = file.getOriginalFilename();
// 调用工具类(文件名,文件数据流)上传到阿里云并返回url地址
String url = minIOUtil.uploadFile(originalFilename, file.getInputStream());
return ApiResult.success(url);
}
@PostMapping("/deleteMinIO")
@Operation(summary = "MinIO文件删除")
public ApiResult<String> delete(@RequestParam("fileUrl") String fileUrl) {
try {
// 调用工具类删除文件
minIOUtil.deleteFileByUrl(fileUrl);
return ApiResult.success("文件删除成功");
} catch (Exception e) {
// 处理异常并返回失败结果
return ApiResult.error("文件删除失败: " + e.getMessage());
}
}
}
参考链接:https://blog.csdn.net/qq_40623672/article/details/140634694
三、配置文件的编写
1. 在application.yml里面配置需要的参数
2. pom.xml文件引入配置文件依赖 不然 @ConfigurationProperties(prefix = "aliyun.oss") 会报红不生效
<!-- 配置生成配置文件处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
3. 使用配置文件里面的信息
方法一:适用于参数多 使用实体类和 @Autowired 引入配置类参数
1)编写config类:注意 @ConfigurationProperties(prefix = "aliyun.oss") 要与第一步yml文件里面的路径匹配
2)使用@Autowired注解,哪里需要就注入引用
方法二:适用于参数少直接使用 @Value("${aliyun.oss.endpoint}") 引入配置类参数(需要注意路径)