在SpringBoot项目中使用阿里云OSS

image.png

环境配置

首先,你需要初始化一个SpringBoot的项目,并引入阿里云OSS的依赖,在本教程中,你还需要引入Lombok。由于此教程需要使用到阿里云OSS,所以开始前请先确保您已经开通阿里云OSS并正确创建Bucket桶。

<!--OSS依赖-->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
</dependency>
<!--Lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
    <version>1.18.32</version>
</dependency>

随后,你需要在application.yml中添加以下配置:

aliyun:
  oss:
    end-point: # 你的阿里云Endpoint,在Bucket创建时会显示
    access-key-id: # 能访问你的Bucket桶的账号所对应的access-key-id
    access-key-secret: # 能访问你的Bucket桶的账号所对应的access-key-secret
    bucket-name: # 创建Bucket时填写的桶名称

相关代码

Step1:配置OSS客户端

首先,创建一个OSSClientConfig.java文件,并输入以下代码。为了防止重复创建多个OSS客户端,我们这里使用了单例模式,将创建的OSS客户端返回。同时,我们需要确保在类销毁时可以正确的关闭客户端,我们需要使用@PreDestory注解,使得客户端可以在类销毁前关闭连接。

@Configuration
@ConfigurationProperties(prefix = "aliyun.oss")
@Data
public class OSSClientConfig {

    // yml中的配置
    private String endPoint;
    private String accessKeyId;
    private String accessKeySecret;

    // 单例模式
    private OSS ossClient;

    @Bean
    public OSS ossClient() {
        // 如果没有对象,直接创建,如果已经存在,不重复生成,直接返回
        if (ossClient == null) {
            ossClient = new OSSClientBuilder().build(endPoint, accessKeyId, accessKeySecret);
        }
        return ossClient;
    }

    @PreDestroy
    public void shutdown() {
        // 如果客户端被创建,则在类销毁前关闭连接
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }
}

Step2:完成文件上传操作

随后,我们需要创建FileService.java接口,及对应的接口实现类FileServiceImpl.java。在这里我们创建了一个upload函数,用于接受前端返回的文件,并在重命名之后上传至服务器中。

public interface FileService {
    String upload(MultipartFile file);
}

@Service
@Slf4j
public class FileServiceImpl implements FileService {

    @Resource
    private OSS ossClient;
    
    @Value("${aliyun.oss.bucket-name}")
    private String bucketName;
    
    /**
     * 阿里云OSS文件上传
     *
     * @param file
     */
    @Override
    public String upload(MultipartFile file) {
        //获取原生文件名
        String originalFilename = file.getOriginalFilename();

        String fileName = generateUUID();
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));

        //在OSS上bucket下的文件名
        String uploadFileName = "blog/" + fileName + extension;

        try {
            PutObjectResult result = ossClient.putObject(bucketName, uploadFileName, file.getInputStream());
            //拼装返回路径
            if (result != null) {
                return result;
            }
        } catch (IOException e) {
            log.error("文件上传失败:{}",e.getMessage());
        }
        return null;
    }
    
    /**
     * 获取随机字符串
     * @return
     */
    private String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }
}

完成这些代码之后,我们可以编写FileController.java,来完成文件上传。

/**
 * 文件接口
 *
*/
@Slf4j
@RestController
@RequestMapping("/file")
public class FileController {

    @Resource
    private FileService fileService;
    
    /**
     * 文件上传接口
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public String upload(@RequestPart("file") MultipartFile file){
        // 出于教学目的,这里没有对文件的大小、文件类型进行校验,读者可以自行添加
        String imgFileStr = fileService.upload(file);
        // 这里可以改为你自行封装的返回类
        return imgFileStr;
    }
}

至此,我们的上传操作已经完成。

Step3:完成文件访问操作

到此,如果你有尝试获取imgFileStr的链接,并直接在浏览器中访问,你会发现,文件是没有办法打开的(如果你设置了私有读写的话)。在OSS中,为了保证数据的安全,我们不能直接读取这些内容,我们需要设置签名,并为其设置具有有效期的访问链接,以供外部访问。由于获取到的链接是动态的,具有有效期,所以我们先指定固定的服务器接口,然后通过服务器重定向到文件的动态链接。

在这里,我们使用了Redis缓存来存储文件的动态地址,在这里我们使用了Redisson来对Redis进行操作。首先,我们在pom.xml中引入Redisson。

<!-- https://github.com/redisson/redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.37.0</version>
</dependency>

随后,我们在application.yml中添加redis的配置信息。

spring:  
    redis:
        database: 1
        host: localhost
        port: 6379
        timeout: 5000
        password: 123456

随后,我们创建RedissonConfig.java来提供服务。

/**
 * Redisson配置
 */
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {

    private String host;

    private Integer port;

    private Integer database;

    private String password;

    private Integer timeout;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://" + host + ":" + port)
                .setDatabase(database)
                .setTimeout(timeout)
                .setPassword(password);
        return Redisson.create(config);
    }

}

在引入了依赖之后,我们对FileServiceImpl.java文件进行修改

@Service
@Slf4j
public class FileServiceImpl implements FileService {
	...
    @Resource
    private RedissonClient redissonClient;

    @Value("${aliyun.oss.bucket-name}")
    private String bucketName;

    @Override
    public String upload(MultipartFile file) {
        ...
        try {
            PutObjectResult result = ossClient.putObject(bucketName, uploadFileName, file.getInputStream());
            //拼装返回路径
            if (result != null) {
                // 这里修改为文件的固定地址
                return uploadFileName;
            }
        } catch (IOException e) {
            log.error("文件上传失败:{}",e.getMessage());
        } 
        ...
    }
    
    /**
     * 获取1小时有效的文件链接,并放入redis缓存中
     * @param fileName
     * @return
     */
    private String generateUrlFromOss(String fileName){
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        String url=ossClient.generatePresignedUrl(bucketName, fileName, expiration).toString();
        saveUrlFromRedis("file:images:"+fileName, url);
        return url;
    }

    private String getUrlFromRedis(String key){
        RBucket<String> bucket = redissonClient.getBucket("file:images:"+key); // 获取RBucket对象
        return bucket.get(); // 获取String值
    }

    private void saveUrlFromRedis(String key, String url){
        RBucket<String> bucket = redissonClient.getBucket(key);
        bucket.set(url, Duration.ofHours(1));
    }

    public String getFileUrl(String fileName){
        return getUrlFromRedis(fileName)==null? generateUrlFromOss(fileName):getUrlFromRedis(fileName);
    }
    ...
}

同时,在FileController.java中添加以下内容。

@Slf4j
@RestController
@RequestMapping("/file")
public class FileController {
    
	...

    // 处理对文件的请求,并重定向到阿里云OSS文件,同时设置缓存头
    @GetMapping("/{prefix}/{fileName:.+}")
    public void redirectToOss(@PathVariable String prefix, @PathVariable String fileName, HttpServletRequest request,HttpServletResponse response) {
        // 获取阿里云OSS文件的签名URL
        String signedUrl = fileService.getFileUrl(prefix+"/"+fileName);

        // 在这种情况下不能使用缓存,因为链接会失效,导致图片无法正常加载!
        // 设置缓存头,缓存30天
        //response.setHeader("Cache-Control", "public, max-age=2592000"); // 30天缓存
        //response.setHeader("Pragma", "cache");  // 兼容旧版浏览器

        // 重定向到阿里云OSS文件的URL
        try {
            response.sendRedirect(signedUrl);
        } catch (Exception e) {
            response.setStatus(404);
        }
    }
}

至此,就可以直接通过重定向的方法从OSS中获取图片了。

发布于:2024年10月27日 23:16