文章目录
- Java实现对象存储的3中方式
- 1、概述
- 2、本地对象存储
- 2.1 配置本地文件相关信息
- 2.2 通用映射配置 ResourcesConfig
- 2.3 文件上传业务 LocalSysFileServiceImpl
- 2.4 上传接口
- 2.5 演示
- 3、MINIO
- 3.1 依赖
- 3.2 配置
- 3.3 配置连接信息
- 3.4. MINIO文件上传业务
- 3.5 文件上传下载接口
- 3.6 演示
- 4、阿里云OSS
- 功能特性:
- 使用流程:
- 安全与合规:
- 步骤1:添加依赖
- 步骤2:配置OSS客户端
- 步骤3:文件上传
- 步骤4:文件下载
- 注意事项
- 5、FastDFS
- 步骤1:引入依赖
- 步骤2:初始化FastDFS客户端配置
- 步骤3:文件上传
- 步骤4:文件下载
- 注意事项
Java实现对象存储的3中方式
1、概述
对象存储(Object Storage)是一种数据存储架构,它以对象(Object)为基本单位管理数据,区别于传统的文件存储(按目录层级组织)和块存储(主要用于磁盘和卷)。在对象存储中,每个对象包含数据本身、可变数量的元数据(描述数据的信息)和一个全局唯一的标识符(通常是对象的名称或键)。这种存储模型非常适合处理非结构化数据,如图片、视频、文档等。
对象存储的主要特点包括:
- 扁平化命名空间:对象存储没有文件系统的目录树结构,所有对象都存储在一个扁平的命名空间(通常称为“容器”或“桶”Bucket)中,通过对象的唯一标识符访问。
- 高度可扩展性:对象存储设计用于海量数据存储,能够容易地水平扩展存储容量,适应不断增长的数据需求。
- 元数据灵活性:每个对象可以携带丰富的元数据,使得数据管理和搜索更加灵活高效。
- 数据冗余与分布:为了确保数据的高可用性和持久性,对象存储通常会在多个地理位置复制数据,即使某个存储节点发生故障也不会丢失数据。
- 基于网络的访问:通过标准的网络协议(如HTTP/HTTPS)和RESTful API访问数据,便于跨平台和远程访问。
- 成本效益:由于其规模经济和优化的存储效率,对象存储通常比其他类型的存储更经济,特别是对于大量非频繁访问的数据(冷数据或温数据)。
- 安全性:对象存储提供多种安全措施,包括数据加密(静态和传输过程中的)、访问控制列表(ACL)、身份验证机制等,以保护数据免受未授权访问。
对象存储广泛应用于云存储服务、大数据分析、内容分发网络(CDN)、备份与归档、以及许多需要处理大量非结构化数据的应用场景中。
2、本地对象存储
2.1 配置本地文件相关信息
# 本地文件存储
file:
# 本地存放的路径地址
path: D:/develop/学习/spring-boot3/file
# 映射地址
prefix: /static
# 访问地址
domain: http://127.0.0.1:${server.port}
我们通过将文件上传到我们指定的本地存放的路径地址,通过访问地址+映射地址+文件名可以直接访问到本地的这个文件
2.2 通用映射配置 ResourcesConfig
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
/**
* 上传文件存储在本地的根路径
*/
@Value("${file.path}")
private String localFilePath;
/**
* 资源映射路径 前缀
*/
@Value("${file.prefix}")
public String localFilePrefix;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** 本地文件上传路径 */
registry.addResourceHandler(localFilePrefix + "/**")
.addResourceLocations("file:" + localFilePath + File.separator);
}
/**
* 开启跨域
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路由
registry.addMapping(localFilePrefix + "/**")
// 设置允许跨域请求的域名
.allowedOrigins("*")
// 设置允许的方法
.allowedMethods("GET");
}
}
2.3 文件上传业务 LocalSysFileServiceImpl
@Primary
@Service
@Slf4j
public class LocalSysFileServiceImpl implements ISysFileService {
/**
* 资源映射路径 前缀
*/
@Value("${file.prefix}")
public String localFilePrefix;
/**
* 域名或本机访问地址
*/
@Value("${file.domain}")
public String domain;
/**
* 上传文件存储在本地的根路径
*/
@Value("${file.path}")
private String localFilePath;
@Value("${server.servlet.context-path}")
private String path;
/**
* 本地文件上传接口
*
* @param file 上传的文件
* @return 访问地址
* @throws Exception
*/
@Override
public String uploadFile(MultipartFile file) {
try {
String name = FileUploadUtils.upload(localFilePath, file);
return domain + path + localFilePrefix + name;
} catch (Exception e) {
log.info("上传文件失败", e);
return "";
}
}
}
2.4 上传接口
@RestController
@Tag(name = "文件控制器")
@RequestMapping("file")
public class SysFileController {
private static final Logger log = LoggerFactory.getLogger(SysFileController.class);
@Autowired
private ISysFileService sysFileService;
/**
* 本地文件上传请求
*/
@Operation(summary = "本地文件上传请求")
@PostMapping("/uploadStatic")
public CommonResult upload(MultipartFile file) {
return CommonResult.SUCCESS(sysFileService.uploadFile(file));
}
}
2.5 演示
上传
访问
3、MINIO
3.1 依赖
<!-- 分布式对象存储minio -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
3.2 配置
# MinIO 分布式文件系统
minio:
url: http://127.0.0.1:9000
# 账号
accessKey: minioadmin
# 密码
secretKey: minioadmin
# MinIO桶名字
bucketName: youzi
3.3 配置连接信息
在代码中配置Access Key ID、Secret Access Key和Endpoint,以建立与对象存储服务的连接。
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
/**
* 服务地址
*/
private String url;
/**
* 用户名
*/
private String accessKey;
/**
* 密码
*/
private String secretKey;
/**
* 存储桶名称
*/
private String bucketName;
@Bean
public MinioClient getMinioClient()
{
return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
}
}
3.4. MINIO文件上传业务
我这里使用一个接口对应多个不同实现类的设计模式典型地体现了**策略模式(Strategy Pattern)**的思想。策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换,让算法的变化独立于使用算法的客户。在这种模式中,一个接口(策略接口)定义了多种行为或算法,而每种行为或算法都有一个具体的实现类。客户端可以通过使用不同的实现类来改变程序的行为,而不需要了解这些类是如何实现的。
/**
* Minio 文件存储
*
* @author ruoyi
*/
@Service
@Slf4j
public class MinioSysFileServiceImpl implements ISysFileService
{
@Autowired
private MinioConfig minioConfig;
@Autowired
private MinioClient client;
/**
* Minio文件上传接口
*
* @param file 上传的文件
* @return 访问地址
* @throws Exception
*/
@Override
public String uploadFile(MultipartFile file){
try {
String path = "test/";
String fileName = path + FileUploadUtils.extractFilename(file);
InputStream inputStream = file.getInputStream();
PutObjectArgs args = PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build();
client.putObject(args);
inputStream.close();
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
} catch (Exception e) {
log.info("上传文件失败", e);
return "";
}
}
@Override
public void downloaFile(String fileName, HttpServletResponse response) {
try {
GetObjectArgs object = GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).build();
InputStream stream = client.getObject(object);
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf-8");
// 设置文件名
String downloadName = fileName.substring(fileName.lastIndexOf("/") + 1);
response.setHeader("Content-Disposition", "attachment;filename=" + downloadName);
OutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = stream.read(buf)) != -1) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.flush();
stream.close();
outputStream.close();
log.info("《==病历Excel文件下载成功,MINIO文件名:{}==》", fileName);
} catch (Exception e) {
System.err.println("Error streaming file: " + e.getMessage());
}
}
}
3.5 文件上传下载接口
@RestController
@Tag(name = "文件控制器")
@RequestMapping("file")
public class SysFileController {
private static final Logger log = LoggerFactory.getLogger(SysFileController.class);
@Autowired
private ISysFileService sysFileService;
@Autowired
@Qualifier("minioSysFileServiceImpl")
private ISysFileService minioSysFileServiceImpl;
/**
* MinIo文件上传请求
*/
@Operation(summary = "MinIo文件上传请求")
@PostMapping("/uploadMinIo")
public CommonResult uploadMinIo(MultipartFile file) {
return CommonResult.SUCCESS(minioSysFileServiceImpl.uploadFile(file));
}
/**
* MinIo文件上传请求
*/
@Operation(summary = "MinIo文件下载")
@PostMapping("/downloadMinIo")
public void downloadMinIo(String fileName, HttpServletResponse response) {
minioSysFileServiceImpl.downloaFile(fileName,response);
}
}
3.6 演示
上传
访问
下载
4、阿里云OSS
阿里云OSS(Object Storage Service)是阿里巴巴云提供的一种海量、安全、低成本、高可靠的云存储服务。OSS专为互联网和移动应用设计,特别适合存储图片、音视频、文档、网站内容等多种类型的数据。以下是使用阿里云OSS的一些关键点和流程:
功能特性:
- 海量存储:支持存储数十亿级别的文件,容量近乎无限扩展。
- 高可靠性:数据多重冗余备份,99.999999999%(11个9)的数据持久性。
- 高速访问:全球CDN加速,提供低延迟、大带宽的数据访问能力。
- 安全性:支持HTTPS、防盗链、STS临时授权、RAM访问控制等安全措施。
- 成本效益:按实际使用量计费,无最低消费限制,且长期存储费用较低。
- 灵活的API访问:提供基于HTTP/HTTPS的RESTful API接口,支持多语言SDK(包括Java、Python、PHP、Node.js等)。
使用流程:
-
注册与实名认证:首先在阿里云官网注册账号并完成实名认证。
-
购买与开通OSS服务:在阿里云控制台购买OSS服务并开通。
-
创建Bucket:登录OSS控制台,创建一个或多个存储空间(Bucket),用于存储对象。
-
上传文件
:通过控制台或者使用SDK编写代码,将文件上传至指定的Bucket中。
- 可以直接上传文件,或使用分片上传处理大文件。
-
管理文件:在控制台上查看、下载、删除文件,或设置访问权限、生命周期规则等。
-
下载与分发:通过URL直接访问或使用CDN加速访问存储在OSS中的文件。
-
API与SDK集成:在应用中集成OSS SDK,实现自动化文件管理,如在上传、下载、删除等操作中动态调用API。
安全与合规:
- 设置合适的访问权限,如公有读私有写、私有读写等。
- 使用RAM(Resource Access Management)进行细粒度的权限控制。
- 开启日志审计,监控和追踪OSS资源访问情况。
步骤1:添加依赖
确保你的项目中已经包含了阿里云OSS SDK的依赖。如果你使用的是Maven,可以在pom.xml
中添加如下依赖:
Xml
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<!-- 请替换为最新的版本号 -->
<version>3.10.2</version>
</dependency>
步骤2:配置OSS客户端
在代码中配置OSS客户端,包括Endpoint、AccessKeyId、AccessKeySecret以及Bucket名称。
Java
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
public class OssDemo {
public static void main(String[] args) {
String endpoint = "<你的Endpoint>";
String accessKeyId = "<你的AccessKeyId>";
String accessKeySecret = "<你的AccessKeySecret>";
String bucketName = "<你的Bucket名称>";
// 创建OSSClient实例
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 进行上传和下载操作...
// 关闭OSSClient
ossClient.shutdown();
}
}
步骤3:文件上传
使用putObject
方法上传文件到OSS。
Java
// 上传文件
String uploadFilePath = "<本地文件路径>";
String objectName = "<OSS中的对象键,即上传后的文件名>";
ossClient.putObject(bucketName, objectName, new File(uploadFilePath));
System.out.println("文件上传成功");
步骤4:文件下载
使用getObject
方法从OSS下载文件到本地。
Java
// 下载文件
String downloadFilePath = "<本地保存路径>";
String downloadObjectName = "<OSS中的对象键,即要下载的文件名>";
ossClient.getObject(new GetObjectRequest(bucketName, downloadObjectName), new File(downloadFilePath));
System.out.println("文件下载成功");
注意事项
- 请确保替换上述代码中的
<你的Endpoint>
、<你的AccessKeyId>
、<你的AccessKeySecret>
、<你的Bucket名称>
、<本地文件路径>
、<OSS中的对象键>
等占位符为实际的值。 - 在生产环境中,建议使用STS(Security Token Service)临时凭证来增强安全性,避免直接使用AccessKey和SecretKey。
- 操作完成后记得关闭
OSSClient
,以释放资源。
以上就是Java程序中使用阿里云OSS实现文件上传和下载的基本步骤。
5、FastDFS
FastDFS,这是一个开源的高性能分布式文件系统,而非FSTFastDfs。在Java程序中使用FastDFS进行文件的上传和下载,通常需要依赖FastDFS的Java客户端库,比如fastdfs-client-java
。
步骤1:引入依赖
如果是Maven项目,你需要在pom.xml
中添加FastDFS客户端的依赖:
Xml
<dependency>
<groupId>org.csource.fastdfs</groupId>
<artifactId>fastdfs-client-java</artifactId>
<!-- 请根据实际情况填写正确的版本号 -->
<version>最新版本号</version>
</dependency>
步骤2:初始化FastDFS客户端配置
在使用FastDFS客户端之前,需要对其进行初始化,通常通过加载配置文件来完成。配置文件中包含Tracker服务器的地址等信息。
Java
import org.csource.fastdfs.*;
public class FastDFSClientExample {
static {
try {
ClientGlobal.init("classpath:fastdfs-client.properties");
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("FastDFS Client Init Fail!", e);
}
}
// ... 其他代码
}
步骤3:文件上传
使用TrackerClient
和StorageClient
来上传文件。
Java
public String uploadFile(String filePath) {
try {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
StorageClient storageClient = new StorageClient(trackerServer, null);
// 上传文件,并获取文件的存储信息(组名和文件路径)
String[] uploadResults = storageClient.upload_file(filePath, "jpg", null);
if (uploadResults != null && uploadResults.length == 2) {
return uploadResults[0] + "/" + uploadResults[1];
} else {
throw new RuntimeException("文件上传失败!");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("文件上传异常!", e);
}
}
步骤4:文件下载
下载文件需要根据文件的存储信息(组名和文件路径)来进行。
Java
public void downloadFile(String groupName, String remoteFileName, String localFilePath) {
try {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
StorageClient storageClient = new StorageClient(trackerServer, null);
// 下载文件到本地
byte[] fileBytes = storageClient.download_file(groupName, remoteFileName);
if (fileBytes != null) {
FileOutputStream out = new FileOutputStream(localFilePath);
out.write(fileBytes);
out.flush();
out.close();
System.out.println("文件下载成功");
} else {
throw new RuntimeException("文件下载失败!");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("文件下载异常!", e);
}
}
注意事项
- 确保
fastdfs-client.properties
配置文件正确配置了Tracker服务器地址。 - 上传和下载的文件名(特别是远程文件名)应遵循FastDFS的规定,有时需要指定文件扩展名以供FastDFS进行正确的MIME类型识别。
- 在实际应用中,应当适当处理异常,并考虑资源的释放和错误的重试逻辑。