云存储优势
- 高可用性:阿里云OSS提供99.999999999%的数据持久性和99.99%的服务可用性。
- 弹性扩展:根据业务需求自动扩展存储容量,无需担心存储瓶颈。
- 安全性:支持数据加密、访问控制、日志记录等安全功能。
- 成本效益:按需付费,降低存储成本。
- 易集成:提供丰富的SDK和API。
注册阿里云账号
如果还没有阿里云账号,需要先注册:
访问 阿里云官网。
点击右上角的 免费注册
,按照提示填写信息,完成账号注册。
注册完成后,登录阿里云控制台。
开通 OSS 服务
登录阿里云官网,在顶部搜索栏检索 对象存储 OSS
。
进入 OSS
控制台页面,点击 立即购买
。
按照提示完成 OSS
服务的开通。
温馨提示
从节省成本考虑,建议购买最低 100G
存储空间,余量不足时可以扩容,当然有条件也可以一次性购买大存储空间。
创建 OSS Bucket
在 OSS
控制台页面,点击左侧菜单的 Bucket
列表。
点击左上角的 创建 Bucket
。
填写 Bucket
信息:
Bucket 名称:自定义一个全局唯一的名称(如 my-springboot-bucket)。
地域:选择离你最近的区域(如 华东1(杭州))。
存储类型:选择标准存储(默认)。
读写权限:选择 私有(推荐)或 公共读(根据需求选择)。
点击 完成创建
,完成 Bucket
创建。
点击 进入Bucket
可以查阅桶信息。
获取 AccessKey
登录阿里云控制台,鼠标悬停在右上角头像上,选择 AccessKey
管理。在 阿里云控制台 创建 AccessKey(AccessKey ID 和 AccessKey Secret)。
点击 创建 AccessKey
。
温馨提示
获取 AccessKey ID
和 AccessKey Secret
,并妥善保存(注意:AccessKey Secret
只会显示一次)。
添加OSS存储依赖
在 pom.xml
中添加阿里云 OSS
的 SDK
依赖:
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.2</version>
</dependency>
在 xiaomayi-common/xiaomayi-oss
模块中已经引入此依赖,在实际使用时直接引入以下依赖即可:
<!-- 阿里云OSS云存储依赖模块 -->
<dependency>
<groupId>com.xiaomayi</groupId>
<artifactId>xiaomayi-oss</artifactId>
</dependency>
配置OSS存储服务
在 application-aliyun.yml
中配置阿里云短信服务的参数:
# 阿里云
aliyun:
# OSS云存储配置
oss:
bucketName: demo2
endpoint: oss.example.com
accessKeySecret: QLGvhLm0ZEYkGlLJMFxkbaISHOmgsN
accessKeyId: LTAI4tMQVr1P2Mu6kXJ0pwgB
创建 OSS
配置类
package com.xiaomayi.oss.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* <p>
* 阿里云OSS云存储配置类
* </p>
*
* @author 小蚂蚁云团队
* @since 2024-05-30
*/
@Configuration
// 指定配置文件位置
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOssConfig {
/**
* 存储桶名称
*/
private static String bucketName;
/**
* 地域节点
*/
private static String endpoint;
/**
* 安全认证ID
*/
private static String accessKeyId;
/**
* 安全认证秘钥
*/
private static String accessKeySecret;
public static String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
AliyunOssConfig.bucketName = bucketName;
}
public static String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
AliyunOssConfig.endpoint = endpoint;
}
public static String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
AliyunOssConfig.accessKeyId = accessKeyId;
}
public static String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
AliyunOssConfig.accessKeySecret = accessKeySecret;
}
}
创建OSS工具类
在 xiaomayi-common/xiaomayi-oss
模块中,已集成 OSS云存储
工具类 AliyunOSSUtil
文件。
package com.xiaomayi.oss.utils;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.xiaomayi.core.utils.DateUtils;
import com.xiaomayi.core.utils.StringUtils;
import com.xiaomayi.oss.config.AliyunOssConfig;
import com.xiaomayi.oss.vo.AliyunOSSVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
/**
* <p>
* 阿里云OSS云存储工具类
* </p>
*
* @author 小蚂蚁云团队
* @since 2024-05-30
*/
@Slf4j
@Component
public class AliyunOSSUtil {
/**
* 允许文件上传后缀
*/
private static final String[] IMAGE_TYPE = new String[]{".bmp", ".jpg", ".jpeg", ".gif", ".png"};
/**
* 创建OSS连接实例
*
* @return 返回结果
*/
private static OSS getOssClient() {
// 获取oss的地域节点
String endpoint = AliyunOssConfig.getEndpoint();
// 获取oss的AccessKeySecret
String accessKeySecret = AliyunOssConfig.getAccessKeySecret();
// 获取oss的AccessKeyId
String accessKeyId = AliyunOssConfig.getAccessKeyId();
ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
// 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
conf.setMaxConnections(200);
// 设置Socket层传输数据的超时时间,默认为50000毫秒。
conf.setSocketTimeout(10000);
// 设置建立连接的超时时间,默认为50000毫秒。
conf.setConnectionTimeout(10000);
// 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
conf.setConnectionRequestTimeout(1000);
// 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
conf.setIdleConnectionTime(10000);
// 设置失败请求重试次数,默认为3次。
conf.setMaxErrorRetry(5);
// 设置是否支持将自定义域名作为Endpoint,默认支持。
conf.setSupportCname(true);
// 设置是否开启二级域名的访问方式,默认不开启。
// conf.setSLDEnabled(true);
// 设置连接OSS所使用的协议(HTTP或HTTPS),默认为HTTP。
conf.setProtocol(Protocol.HTTPS);
// 设置用户代理,指HTTP的User-Agent头,默认为aliyun-sdk-java。
/*conf.setUserAgent("aliyun-sdk-java");
// 设置代理服务器端口。
conf.setProxyHost("<yourProxyHost>");
// 设置代理服务器验证的用户名。
conf.setProxyUsername("<yourProxyUserName>");
// 设置代理服务器验证的密码。
conf.setProxyPassword("<yourProxyPassword>");*/
// 设置是否开启HTTP重定向,默认开启。
conf.setRedirectEnable(true);
// 设置是否开启SSL证书校验,默认开启。
conf.setVerifySSLEnable(true);
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, conf);
return ossClient;
}
/**
* 根据文件路径获取OSS访问地址
* 备注:需要对中文路径进行URL编码
*
* @param filePath 文件路径
* @return 返回结果
*/
public static String getFileURL(String filePath) {
// 地域节点
String endpoint = AliyunOssConfig.getEndpoint();
// 判断文件路径是否已包含
if (filePath.contains(endpoint)) {
// 直接返回
return filePath;
}
String encode = null;
try {
// 文件路径分裂处理
String[] strings = filePath.split("/");
// 获取文件路径和文件名
String lastIndex = strings[strings.length - 1];
// URL编码
lastIndex = URLEncoder.encode(lastIndex, "utf-8");
// 重新赋值
strings[strings.length - 1] = lastIndex;
// 数组重新已斜杠拼接
encode = String.join("/", strings);
} catch (UnsupportedEncodingException e) {
log.error("文件处理错误:{}", e.getMessage());
}
// 返回结果
return String.format("%s://%s/%s", Protocol.HTTPS, AliyunOssConfig.getEndpoint(), encode);
}
/**
* 上传文件
*
* @param uploadFile 文件对象
* @param folder 文件夹
* @param fileName 文件名
* @return 返回结果
*/
public static AliyunOSSVO upload(MultipartFile uploadFile, String folder, String fileName) {
// 获取OSS存储桶名称
String bucketName = AliyunOssConfig.getBucketName();
// 获取文件原名称
String originalFilename = uploadFile.getOriginalFilename();
// 构建日期路径, 例如:OSS目标文件夹/2024/05/30/文件名
// 文件上传的路径地址
String filePath = folder + "/" + fileName;
// 获取文件输入流
InputStream inputStream = null;
try {
inputStream = uploadFile.getInputStream();
} catch (IOException e) {
log.error("上传文件异常:{}", e.getMessage());
}
/*
* 下面两行代码是重点坑:
* 现在阿里云OSS 默认图片上传ContentType是image/jpeg
* 也就是说,获取图片链接后,图片是下载链接,而并非在线浏览链接,
* 因此,这里在上传的时候要解决ContentType的问题,将其改为image/jpg
*/
ObjectMetadata metadata = new ObjectMetadata();
for (String type : IMAGE_TYPE) {
if (StringUtils.endsWithIgnoreCase(uploadFile.getOriginalFilename(), type)) {
metadata.setContentType("image/jpg");
}
}
// 获取阿里云OSS实例
OSS ossClient = getOssClient();
// 文件上传至阿里云OSS
ossClient.putObject(bucketName, filePath, inputStream, metadata);
// 获取文件上传后的图片返回地址
String fileUrl = getFileURL(filePath);
// 实例化VO对象
AliyunOSSVO aliyunOSSVO = new AliyunOSSVO();
aliyunOSSVO.setFileName(originalFilename);
aliyunOSSVO.setFilePath(filePath);
aliyunOSSVO.setFileUrl(fileUrl);
// 返回结果
return aliyunOSSVO;
}
/**
* 上传文件至OSS
*
* @param filePath 文件完整路径
* @param folder 文件夹
* @param delFile 是否删除
* @return 返回结果
*/
public static AliyunOSSVO uploadWithOss(String filePath, String folder, Boolean delFile) {
// 实例化文件
File file = new File(filePath);
// 判断文件是否存在
if (!file.exists()) {
return null;
}
// 原始文件名
String fileName = file.getName();
// 获取OSS存储桶名称
String bucketName = AliyunOssConfig.getBucketName();
// 日期文件夹路径
String datePath = DateUtils.datePath();
// 准备上传OSS的文件路径和文件名
String objectName = folder + "/" + datePath + "/" + fileName;
// 实例化文件流
FileInputStream fileInputStream = null;
try {
// 根据文件创建文件流
fileInputStream = new FileInputStream(file);
// 获取OSS云存储实例
OSS ossClient = getOssClient();
// 文件上传至阿里云OSS
ossClient.putObject(bucketName, objectName, fileInputStream);
} catch (FileNotFoundException e) {
log.error("文件上传失败:{}", e.getMessage());
} finally {
try {
if (null != fileInputStream) {
// 关闭文件流
fileInputStream.close();
// 删除文件
if (delFile) {
file.delete();
}
}
} catch (IOException e) {
log.error("文件流关闭失败:{}", e.getMessage());
}
}
// 获取文件上传后的地址
String fileUrl = getFileURL(objectName);
// 实例化VO对象
AliyunOSSVO aliyunOSSVO = new AliyunOSSVO();
aliyunOSSVO.setFileName(fileName);
aliyunOSSVO.setFilePath(filePath);
aliyunOSSVO.setFileUrl(fileUrl);
// 返回结果
return aliyunOSSVO;
}
/**
* 上传文件流至OSS
*
* @param inputStream 文件流
* @param folder 文件夹
* @param fileName 文件名称
* @return 返回结果
*/
public static AliyunOSSVO uploadWithInputStream(InputStream inputStream, String folder, String fileName) {
// 获取OSS存储桶名称
String bucketName = AliyunOssConfig.getBucketName();
// 新文件名称
String filePath = String.format("%s/%s/%s", folder, DateUtils.datePath(), fileName);
try {
// 获取OSS存储实例
OSS ossClient = getOssClient();
// 文件上传至阿里云OSS
ossClient.putObject(bucketName, filePath, inputStream);
} catch (OSSException oe) {
log.error("文件上传失败:{}", oe.getMessage());
} finally {
try {
if (null != inputStream) {
// 关闭文件流
inputStream.close();
}
} catch (IOException e) {
log.error("文件流关闭失败:{}", e.getMessage());
}
}
// 获取文件上传后的地址
String fileUrl = getFileURL(filePath);
// 实例化VO对象
AliyunOSSVO aliyunOSSVO = new AliyunOSSVO();
aliyunOSSVO.setFileName(fileName);
aliyunOSSVO.setFilePath(filePath);
aliyunOSSVO.setFileUrl(fileUrl);
// 返回结果
return aliyunOSSVO;
}
/**
* 下载远程OSS文件至本地
*
* @param filePath OSS文件路径
* @param newFile 目标文件
*/
public static void writeLocalFile(String filePath, String newFile) {
try {
// 实例化目标文件
File destFile = new File(newFile);
// 创建目录
if (destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
// 实例化OSS客户端实例
OSS ossClient = getOssClient();
// 获取OSS云存储桶名称
String bucketName = AliyunOssConfig.getBucketName();
// 根据存储桶名称、文件路径获取OSS文件对象
OSSObject ossObject = ossClient.getObject(bucketName, filePath);
// 读取文件内容
InputStream inputStream = ossObject.getObjectContent();
if (null != inputStream) {
// 把输入流放入缓存流
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
// 实例化目标文件流
FileOutputStream fileOutputStream = new FileOutputStream(newFile);
// 把输出流放入缓存流
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
byte[] buffer = new byte[1024];
int len = 0;
while ((len = bufferedInputStream.read(buffer)) != -1) {
bufferedOutputStream.write(buffer, 0, len);
}
bufferedOutputStream.flush();
bufferedOutputStream.close();
bufferedInputStream.close();
}
} catch (Exception e) {
log.error("文件写入本地失败:{}", e.getMessage());
}
}
/**
* 删除OSS文件
*
* @param filePath OSS文件路径
* @return 返回结果
*/
public static Boolean delete(String filePath) {
try {
// 获取OSS云存储实例
OSS ossClient = getOssClient();
// 获取OSS云存储桶名称
String bucketName = AliyunOssConfig.getBucketName();
// 从OSS存储桶中删除指定文件
ossClient.deleteObject(bucketName, filePath);
// 返回结果
return true;
} catch (Exception e) {
log.error("删除文件失败:{}", e.getMessage());
// 返回结果
return false;
}
}
/**
* OSS云存储文件拷贝
*
* @param oldFilePath 原文件
* @param destFilePath 目标文件
* @return 返回结果
*/
public static boolean copyFile(String oldFilePath, String destFilePath) {
// 原文件判空
if (StringUtils.isEmpty(oldFilePath)) {
return false;
}
// 目标文件判空
if (StringUtils.isEmpty(destFilePath)) {
return false;
}
try {
// 获取OSS云存储桶名称
String bucketName = AliyunOssConfig.getBucketName();
// 获取OSS云存储客户端实例
OSS ossClient = getOssClient();
// 判断存储桶中是否次存在原文件
if (ossClient.doesObjectExist(bucketName, oldFilePath)) {
// 从存储桶A拷贝文件A至存储桶B文件B
ossClient.copyObject(bucketName, oldFilePath, bucketName, destFilePath);
}
return true;
} catch (OSSException e) {
log.error("文件拷贝失败:{}", e.getMessage());
}
return false;
}
}
测试OSS云存储
以下是官方编写的创建文件上传、下载、删除等案例;
package com.xiaomayi.admin.controller.demo;
import com.xiaomayi.core.utils.R;
import com.xiaomayi.oss.utils.AliyunOSSUtil;
import com.xiaomayi.oss.vo.AliyunOSSVO;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* <p>
* 案例测试 前端控制器
* </p>
*
* @author 小蚂蚁云团队
* @since 2024-05-26
*/
@Slf4j
@RestController
@RequestMapping("/oss")
@AllArgsConstructor
public class OSSController {
/**
* 上传文件至OSS云存储
*
* @param file 文件对象
* @return 返回结果
*/
@PostMapping("/uploadFile")
public R uploadFile(@RequestParam("file") MultipartFile file) {
// 定义存储目录
String folder = "test";
// 定义新文件名
String newFileName = file.getOriginalFilename();
// 上传文件
AliyunOSSVO aliyunOSSVO = AliyunOSSUtil.upload(file, folder, newFileName);
// 返回结果
return R.ok(aliyunOSSVO);
}
/**
* 文件下载
*
* @return 返回结果
*/
@GetMapping("/downloadFile")
public R downloadFile() {
// OSS文件地址
String ossFilePath = "test/logo.jpg";
// 本地文件地址
String filePath = "E:/logo.jpg";
// 读取OSS文件并写入本地文件
AliyunOSSUtil.writeLocalFile(ossFilePath, filePath);
return R.ok();
}
/**
* 文件删除
*
* @return 返回结果
*/
@DeleteMapping("/deleteFile")
public R deleteFile() {
AliyunOSSUtil.delete("test/logo.jpg");
return R.ok();
}
}
文件上传 OSS
成功后输出结果:
{
"code": 0,
"msg": "操作成功",
"data": {
"fileName": "logo.jpg",
"filePath": "test/logo.jpg",
"fileUrl": "https://oss-cn-shanghai.aliyuncs.com/test/logo.jpg"
},
"ok": true
}
前往阿里云OSS云存储对应的 xm-example
存储桶中查看,可以看到文件已上传成功,如下图:
温馨提示
本章节详细的阐述了后端服务是如何上传、下载、删除文件,实际使用时可以直接在前端应用中对接阿里云 OSS云存储
,直接将上传成功后的文件地址提交后端服务保存即可,避免造成应用服务器压力过载。
总结
通过以上步骤,你可以成功将阿里云 OSS
集成到项目中,实现文件的上传、下载和删除功能。
注意事项
AccessKey 安全:不要将 AccessKey 直接暴露在前端代码中,确保后端安全存储。
Bucket 权限:根据业务需求设置 Bucket 的读写权限(私有、公共读、公共读写)。
文件大小限制:OSS 默认支持最大 5TB 的文件,但上传时需注意网络带宽和超时问题。
费用:OSS 按存储量、流量、请求次数等收费,使用前请了解相关计费规则。