OSS使用入门
1.1. 结构及功能
目录结构
oss
└─ config
└─ OssConfig
└─ service
└─ OssService
└─ OssServiceImpl
└─ utils
└─ OssUtils
功能
- 操作桶相关(桶是否存在,新建桶,删除桶,获取所有桶,获取桶内对象信息)
- 对象操作:
- 上传到指定的桶的指定位置
- 下载指定桶内的指定文件
- 删除指定文件
- 获取文件的url
- 获取文件的二进制数组
- 判断对象是否存在
- 工具
- 对图片附件进行压缩,可以按最大尺寸或按质量压缩
1.2. 配置
pom.xml
<!--OSS核心-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<!--jdk9以上版本需要以下三个-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
<!--注解相关-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--编解码工具-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.21</version>
</dependency>
<!--图片处理-->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- 添加slf4j日志api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- 添加logback-classic依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 添加logback-core依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
application.yml
spring:
#上传文件大小限制
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
oss:
# 阿里云oss节点,根据实际情况选择
endpoint: https://oss-cn-qingdao.aliyuncs.com
# 桶名字
bucketName: 实际
accessKeyId: 实际
accessKeySecret: 实际
1.3. 其他代码
OssConfig.java 配置Bean
@Data
@Configuration
@ConfigurationProperties(prefix = "oss")
public class OssConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
@Bean
public OSS OssClient() {
return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
}
}
OssService.java 业务接口
/**
* 阿里云OSS业务接口
*
* @Author Mosfield
*/
public interface OssService {
//////////////////// 桶操作相关 /////////////////////
/**
* 查看bucket是否存在
*
* @param bucketName
* @return
*/
Boolean bucketExists(String bucketName);
/**
* 创建存储bucket
*
* @param bucketName
*/
void makeBucket(String bucketName);
/**
* 删除存储bucket
*
* @param bucketName
*/
void removeBucket(String bucketName);
/**
* 获取全部bucket
*
* @return
*/
List<Bucket> listBuckets();
/**
* 递归查看桶内的所有文件信息
*
* @return 存储bucket内文件对象信息
*/
List<String> listObjects();
/**
* 列出某个存储桶中的所有对象。
*
* @param bucketName 存储桶名称
* @param prefix 对象名称的前缀
* @return
*/
List<String> listObjects(String bucketName, String prefix) ;
//////////////////// 对象操作相关 /////////////////////
/**
* 判断文件是否存在
*
* @param fileName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @return true存在, 反之
*/
Boolean checkFileIsExist(String fileName);
/**
* 判断文件是否存在
*
* @param fileName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @param bucket 桶名称
* @return true存在, 反之
*/
Boolean checkFileIsExist(String fileName, String bucket);
/**
* 删除默认桶内的指定文件
*
* @param fileName
* @return
* @throws Exception
*/
void remove(String fileName);
/**
* 删除指定桶内的指定文件
*
* @param fileName
* @return
* @throws Exception
*/
void remove(String fileName, String bucket);
/**
* 把文件上传到默认桶,名字为原名字
*
* @param file
* @return
*/
String uploadFileObject(MultipartFile file);
/**
* 把文件上传到默认桶
*
* @param file
* @param name 路径和新名字
* @return
*/
String uploadFileObject(MultipartFile file, String name);
/**
* 把文件上传到指定桶的指定路径下,桶名为空则上传到默认桶,路径为空则上传到桶的根目录
*
* @param file
* @param name
* @param bucket
* @return
*/
String uploadFileObject(MultipartFile file, String name, String bucket);
/**
* 把文件流上传到默认桶
*
* @param inputStream
* @param name 路径和新名字
* @return
*/
String uploadFileObject(InputStream inputStream, String name);
/**
* 把文件流上传到指定桶
*
* @param inputStream
* @param name 路径和新名字
* @return
*/
String uploadFileObject(InputStream inputStream, String name, String bucket);
/**
* 从默认的桶里下载文件
*
* @param fileName
* @return
*/
InputStream downloadFileObject(String fileName);
/**
* 从指定的桶里下载文件
*
* @param fileName
* @param bucket
* @return
*/
InputStream downloadFileObject(String fileName, String bucket);
/**
* 获取文件的url
*
* @param fileName
* @return
*/
String getPresignedObjectUrl(String fileName);
/**
* 从指定的桶中获取文件的url
*
* @param fileName
* @return
*/
String getPresignedObjectUrl(String fileName, String bucket);
/**
* 获取公开桶里文件的url
*
* @param fileName
* @return
*/
String getPublicUrlByName(String fileName);
/**
* 获取指定的公开桶里文件的url
*
* @param fileName
* @return
*/
String getPublicUrlByName(String fileName, String bucket);
/**
* 根据路径和名字,获取默认桶内对应的二进制数组
*
* @param fileName
* @return
*/
byte[] getByteArrByName(String fileName);
/**
* 根据路径和名字,获取指定桶内对应的二进制数组
*
* @param fileName
* @param bucket
* @return
*/
byte[] getByteArrByName(String fileName, String bucket);
}
OssServiceImpl.java 业务实现类
/**
* OSS业务实现类
*
* @Author Mosfield
* @Date 2023-6-5
*/
@Service
@RequiredArgsConstructor
public class OssServiceImpl implements OssService {
/**
* OSS客户端单例
*/
private final OSS ossClient;
/**
* OSS配置信息
*/
private final OssConfig ossConfig;
@Override
@SneakyThrows
public Boolean bucketExists(String bucketName) {
return ossClient.doesBucketExist(bucketName);
}
@Override
@SneakyThrows
public void makeBucket(String bucketName) {
// 创建CreateBucketRequest对象。
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
// 如果创建存储空间的同时需要指定存储类型、存储空间的读写权限、数据容灾类型, 请参考如下代码。
// 此处以设置存储空间的存储类型为标准存储为例介绍。
//createBucketRequest.setStorageClass(StorageClass.Standard);
// 数据容灾类型默认为本地冗余存储,即DataRedundancyType.LRS。如果需要设置数据容灾类型为同城冗余存储,请设置为DataRedundancyType.ZRS。
//createBucketRequest.setDataRedundancyType(DataRedundancyType.ZRS);
// 设置存储空间读写权限为公共读,默认为私有。
//createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
// 在支持资源组的地域创建Bucket时,您可以为Bucket配置资源组。
//createBucketRequest.setResourceGroupId(rsId);
// 创建存储空间
ossClient.createBucket(createBucketRequest);
}
@Override
@SneakyThrows
public void removeBucket(String bucketName) {
// 删除存储空间。
ossClient.deleteBucket(bucketName);
}
@Override
@SneakyThrows
public List<Bucket> listBuckets() {
return ossClient.listBuckets();
}
@Override
@SneakyThrows
public List<String> listObjects() {
return this.listObjects(ossConfig.getBucketName(), "");
}
@Override
@SneakyThrows
public List<String> listObjects(String bucketName, String prefix) {
List<String> res = new ArrayList<>();
// 构造ListObjectsRequest请求。
ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);
if (prefix == null) {
prefix = "";
}
// 设置prefix参数来获取fun目录下的所有文件。
listObjectsRequest.setPrefix(prefix);
// 列出文件。
ObjectListing listing = ossClient.listObjects(listObjectsRequest);
// 遍历所有文件
for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) {
System.out.println(objectSummary.getKey());
}
return res;
}
@Override
@SneakyThrows
public Boolean checkFileIsExist(String fileName) {
return this.checkFileIsExist(fileName, ossConfig.getBucketName());
}
@Override
@SneakyThrows
public Boolean checkFileIsExist(String fileName, String bucket) {
if (fileName == null || "".equals(fileName)) {
return false;
}
System.out.println(ossConfig);
System.out.println(ossClient);
return ossClient.doesObjectExist(bucket, fileName);
}
@Override
@SneakyThrows
public void remove(String fileName) {
this.remove(fileName, ossConfig.getBucketName());
}
@Override
@SneakyThrows
public void remove(String fileName, String bucket) {
ossClient.deleteObject(bucket, fileName);
}
@Override
@SneakyThrows
public String uploadFileObject(MultipartFile file) {
return this.uploadFileObject(file, file.getOriginalFilename(), ossConfig.getBucketName());
}
@Override
@SneakyThrows
public String uploadFileObject(MultipartFile file, String name) {
return this.uploadFileObject(file, name, ossConfig.getBucketName());
}
@Override
@SneakyThrows
public String uploadFileObject(MultipartFile file, String name, String bucket) {
if (file.isEmpty()) {
return null;
}
return this.uploadFileObject(file.getInputStream(), name, ossConfig.getBucketName());
}
@Override
@SneakyThrow
public String uploadFileObject(InputStream inputStream, String name) {
return this.uploadFileObject(inputStream, name, ossConfig.getBucketName());
}
@Override
@SneakyThrow
public String uploadFileObject(InputStream inputStream, String name, String bucket) {
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, name, inputStream);
// 创建PutObject请求。
ossClient.putObject(putObjectRequest);
// 返回访问的url
return this.getPresignedObjectUrl(name);
}
@Override
@SneakyThrows
public InputStream downloadFileObject(String fileName) {
return this.downloadFileObject(fileName, ossConfig.getBucketName());
}
@Override
@SneakyThrows
public InputStream downloadFileObject(String fileName, String bucket) {
// 先判断是否存在该文件,不存在返回null
Boolean result = this.checkFileIsExist(fileName, bucket);
if (!result) {
return null;
}
return ossClient.getObject(bucket, fileName).getObjectContent();
}
@Override
@SneakyThrows
public String getPresignedObjectUrl(String fileName) {
return this.getPresignedObjectUrl(fileName, ossConfig.getBucketName());
}
@Override
@SneakyThrows
public String getPresignedObjectUrl(String fileName, String bucket) {
// 先判断是否存在该文件,不存在返回null
Boolean result = this.checkFileIsExist(fileName, bucket);
if (!result) {
return null;
}
// 设置签名URL过期时间,单位为毫秒。
Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
// 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
URL url = ossClient.generatePresignedUrl(bucket, fileName, expiration);
return url.toString();
}
@Override
@SneakyThrows
public String getPublicUrlByName(String fileName) {
return this.getPublicUrlByName(fileName, ossConfig.getBucketName());
}
@Override
@SneakyThrows
public String getPublicUrlByName(String fileName, String bucket) {
String url = "https://" + bucket + "."
+ ossConfig.getEndpoint().split("https://")[1]
+ "/" + fileName; //文件名
return url;
}
@Override
@SneakyThrows
public byte[] getByteArrByName(String fileName) {
return this.getByteArrByName(fileName, ossConfig.getBucketName());
}
@Override
@SneakyThrows
public byte[] getByteArrByName(String fileName, String bucket) {
// 先判断是否存在该文件,不存在返回null
Boolean result = this.checkFileIsExist(fileName, bucket);
if (!result) {
return null;
}
InputStream inputStream = this.downloadFileObject(fileName, bucket);
return IOUtils.toByteArray(inputStream);
}
}
OssUtils.java 工具类(选用)
功能:
- 压缩图片(也可以不使用这个方法,使用阿里云提供的图片压缩方法)
- 根据文件原名获取根据uuid生成的随机文件名。
@Slf4j
public class OssUtils {
/**
* 压缩图片
*
* @param file 原始文件
* @param maxWidth 最大宽
* @param maxHeight 最大高
* @param quality 质量压缩比,范围是(0,1]
* @return
*/
public static MultipartFile zipImg(MultipartFile file, Integer maxWidth, Integer maxHeight, Float quality) throws IOException {
String fileName = file.getName();
String originalFilename = file.getOriginalFilename();
String contentType = file.getContentType();
InputStream is = null;
// 通过MultipartFile得到InputStream,从而得到BufferedImage
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
// 获取图片的真实长和宽
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
if (maxWidth == null) {
maxWidth = width;
}
if (maxHeight == null) {
maxHeight = height;
}
// 计算图片的压缩比
float rate = 1f;
if (width > maxWidth) {
rate = 1f * maxWidth / width;
}
if (height > maxHeight && rate > 1f * maxHeight / height) {
rate = 1f * maxHeight / height;
}
log.info("压缩比" + rate);
// 质量压缩比
if(quality == null){
quality=1f;
}
is = file.getInputStream();
// 此处换成任意路径即可,临时文件存放位置
File tempFile = new File("/" + originalFilename);
Thumbnails.of(is)
.scale(rate)
.outputQuality(quality)
.toFile(tempFile);
FileInputStream fileInputStream = new FileInputStream(tempFile);
MockMultipartFile newFile = new MockMultipartFile(fileName, originalFilename, contentType, fileInputStream);
fileInputStream.close();
// 删除临时文件
boolean success = tempFile.delete();
log.info("压缩结果:" + success);
return newFile;
}
/**
* 通过UUID的规则生成新的文件名,并在前面拼接上路径前缀
* @param fileName 原文件名
* @return
*/
public static String getNewFileNameByUUID(String fileName) {
return OssUtils.getNewFileNameByUUID(fileName,"");
}
/**
* 通过UUID的规则生成新的文件名,并在前面拼接上路径前缀
*
* @param fileName 原文件名
* @param prefix 路径名可以为空。如果不为空,那不应该以/开头,应该以/结尾
* @return
*/
public static String getNewFileNameByUUID(String fileName, String prefix) {
// 处理前缀为null的情况
if (prefix == null) {
prefix = "";
}
// 去除空格
prefix = prefix.trim();
// 判断前缀是否以斜线开头,如果是,就删除开头的斜线
if (prefix.startsWith("/")) {
prefix = prefix.replaceFirst("/", "");
}
// 判断如果内容不为空,且不以斜线结尾,则在尾部加上斜线
if (prefix.length() > 0 && !prefix.endsWith("/")) {
prefix = prefix + "/";
}
return prefix + UUID.randomUUID().toString().replaceAll("-", "")
+ fileName.substring(fileName.lastIndexOf("."));
}
}
1.4. 案例实现-图片上传和展示
UserController.java
/**
* 用户相关controller
*
* @Author Mosfield
* @Date 2023-6-1
*/
@RestController
@CrossOrigin
@RequestMapping("user")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
/**
* 根据用户id查询具体用户
*
* @param id
* @return
*/
@RequestMapping("/loadUserById")
public Result loadUserById(@RequestParam("id") Integer id) {
User user = userService.loadUserById(id);
return Result.success(user);
}
/**
* 把文件附件上传到oss
* @param file
* @param id
* @return
*/
@PostMapping("uploadImgToOss")
public Result uploadImgToOss(@RequestParam("image") MultipartFile file, @RequestParam(name = "id", required = false) Integer id) {
if (file == null) {
return AjaxResult.failure("文件上传失败!");
}
String fileName = userService.uploadImgToOss(file,id);
return AjaxResult.getResult(fileName);
}
}
UserServiceImpl.java (接口省略)
/**
* 用户业务层实现类
* @Author Mosfield
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private OssService ossService;
/**
* 根据id查询用户
* @param id
* @return
*/
@Override
public User loadUserById(Integer id) {
// 查询数据库,获得user对象
User user = userMapper.loadUserById(id);
// 查询oss,把图片的二进制数组放入user对象
user.setImgByte(ossService.getByteArrByName(user.getHeadImg()));
return user;
}
/**
* 把文件附件上传到oss,返回图片路径
* @param file
* @param id
* @return
*/
@Override
@SneakyThrows
public String uploadImgToOss(MultipartFile file, Integer id) {
log.info("关联id:" + id);
log.info("文件原名:" + file.getOriginalFilename());
// 文件原名
String fileName = file.getOriginalFilename();
// 新文件名:路径+UUID随机文件名+原拓展名
String newName = OssUtils.getNewFileNameByUUID(fileName, "tb_user/img/");
log.info("文件新名:" + newName);
// 压缩图片
MultipartFile newFile = OssUtils.zipImg(file, 500, 500, null);
try {
// 存储
ossService.uploadFileObject(newFile, newName);
} catch (Exception e) {
// 自定义业务异常
throw new FileUploadFailedException("文件上传失败");
}
return newName;
}
}
img展示二进制数据图片(element-ui组件)
<el-image :src="'data:image/png;base64,'+ 二进制数组变量" fit="contain">
</el-image>
也可以使用 OssService 中的几个获取url的方法,返回oss对应的外部访问链接。
需要注意的是,要确认浏览器能访问到oss提供的地址,同时需要注意oss分享的连接的过期时间。
Comments NOTHING