Spring Boot 集成阿里云OSS 完成文件上传下载

前言:

文件上传下载在项目开发中是一个非常常见的业务场景,在云服务上还没有兴起的时候,一般来说都会把文件单独存放到文件服务器上,随着云服务的兴起,各类云服务厂商都提供了 OSS 服务,本篇我们分享 Spring Boot 项目如何把文件存储到阿里云 OSS。

Spring Boot 集成阿里云 OSS

阿里云提供了 SDK,项目中引入相关依赖即可,我们在 pom.xml 文件中引入依赖如下:

xml 复制代码
<dependency>
	<groupId>com.aliyun.oss</groupId>
	<artifactId>aliyun-sdk-oss</artifactId>
	<version>3.15.1</version>
</dependency>

阿里云访问信息

阿里云访问信息有四个如下:

  • Endpoint:OSS服务所在地域的访问域名。
  • AccessKeyId:访问OSS服务的密钥ID。
  • AccessKeySecret:访问OSS服务的密钥秘钥。
  • BucketName:您创建的存储空间名称。

阿里云 OSS 上传下载工具类

根据阿里云的访问要求封装了阿里云 OSS 上传下载工具类,如下:

java 复制代码
import cn.hutool.core.util.StrUtil;
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.GetObjectRequest;
import com.aliyun.oss.model.PutObjectRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Date;

/**
 * @ClassName: AliyunOssUtil
 * @Author: Author
 * @Date: 2024/11/13 19:39
 * @Description:
 */
@Slf4j
@Component
public class AliyunOssUtil {

    @Value("${aliyun.oss.endpoint}")
    private String endpoint;

    @Value("${aliyun.oss.accessKeyId}")
    private String accessKeyId;

    @Value("${aliyun.oss.accessKeySecret}")
    private String accessKeySecret;

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

    /**
     * @return com.aliyun.oss.ClientBuilderConfiguration
     * @description 获取配置类
     */
    public ClientBuilderConfiguration getConfig() {
        // ClientBuilderConfiguration是OSSClient的配置类,可配置代理、连接超时、最大连接数等参数。
        ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
        // 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
        conf.setConnectionRequestTimeout(3000);
        // 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
        conf.setIdleConnectionTime(30000);
        conf.setProtocol(Protocol.HTTPS);
        return conf;
    }


    /**
     * @param file:
     * @param fileName:
     * @return java.lang.String
     * @description
     */
    public String uploadOssFile(File file, String fileName) throws IOException {
        // 获取上传的文件的输入流
        InputStream in = new FileInputStream(file);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, in);
        String fileUrl = null;
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, getConfig());
        try {
            // 上传文件
            ossClient.putObject(putObjectRequest);
            // 获取文件访问路径
            Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
            URL url = ossClient.generatePresignedUrl(bucketName, fileName, expiration);
            //url 解码 返回的地址需要进行 URL 解码
            fileUrl = URLDecoder.decode(url.toString(), "UTF-8");
        } catch (OSSException e) {
            log.error("oss上传文件失败,异常信息:", e);
        } finally {
            if (ossClient != null) {
                // 关闭ossClient
                ossClient.shutdown();
            }
        }
        return fileUrl;
    }

    /**
     * @param fileUrl:
     * @param fileName:
     * @return java.io.File
     * @description
     */
    public File downLoadOssFile(String fileUrl, String fileName) throws IOException {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, getConfig());
        File file = new File(fileName);
        if (!file.exists()) {
            file.createNewFile();
        }
        String[] array = fileUrl.split("[?]");
        fileUrl = array[0];
        //key 填写不包含 Bucket 名称在内的路径  例如 testfolder/mytest.xlsx
        String key = fileUrl.substring(fileUrl.lastIndexOf(StrUtil.SLASH) + 1);
        ossClient.getObject(new GetObjectRequest(bucketName, key), file);
        ossClient.shutdown();
        return file;
    }


    /**
     * oss中文件是否存在
     *
     * @param fileName
     * @return
     */
    public Boolean isFileExist(String fileName) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, getConfig());
        Boolean result = Boolean.FALSE;
        try {
            result = ossClient.doesObjectExist(bucketName, fileName);
        } catch (OSSException oe) {
            log.error("oss检验文件是否存在失败,Error Message:{},Error Code:{}", oe.getMessage(), oe.getErrorCode());
        } finally {
            if (ossClient != null) {
                // 关闭ossClient
                ossClient.shutdown();
            }
        }
        return result;
    }

    /**
     * 删除oss文件
     *
     * @param fileName
     */
    public void deleteFile(String fileName) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, getConfig());
        try {
            // 删除文件
            ossClient.deleteObject(bucketName, fileName);
        } catch (OSSException oe) {
            log.error("oss删除文件失败,Error Message:{},Error Code:{}", oe.getMessage(), oe.getErrorCode());
        } finally {
            if (ossClient != null) {
                // 关闭ossClient
                ossClient.shutdown();
            }
        }
    }


}

业务场景

业务场景要求用户端发起导出请求后,快速生成一个导出记录响应到用户端,后端异步完成导出操作,后端完成导出后,将导出的文件上传到阿里云 OSS,用户可以在页面完成文件的下载。

前面我们已经封装好了阿里云 OSS 的工具类,这里我们实现整个业务,调用 OSS 工具类完成文件的上传下载即可。

Service 代码如下

部分项目中的代码没有展示出来,了解整体实现思路即可。。

java 复制代码
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @ClassName: FileServiceImpl
 * @Author: Author
 * @Date: 2024/11/13 19:39
 * @Description:
 */
@Slf4j
@Service
public class FileServiceImpl implements IFlieService {

    @Autowired
    private AibabaCloudFileMapper aibabaCloudFileMapper;

    @Autowired
    private AliyunOssUtil aliyunOssUtil;

    //前面我们已经封装好了阿里云 OSS 的工具类,这里我们实现整个业务,调用 OSS 工具类完成文件的上传下载即可。

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void exportList(FileQueryDTO fileQueryDTO) {
        List<SourceCodeAnalysisExportVO> exportList = new ArrayList<>();
        //导出记录落库
        AlibabaCloudFileDO alibabaCloudFileDO = new AlibabaCloudFileDO();
        //文件业务类型
        alibabaCloudFileDO.setBusinessType(1);
        //文件生成中
        alibabaCloudFileDO.setFileStatus(1);
        String fileName = "导出学生成绩单" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
        alibabaCloudFileDO.setFileName(fileName);
        aibabaCloudFileMapper.insert(alibabaCloudFileDO);
        uploadFileAliOss(exportList, fileName, alibabaCloudFileDO.getId());
    }

    @Override
    public HttpServletResponse downLoadSouceCodeAnalysisFile(Long id, HttpServletResponse response) {
        AlibabaCloudFileDO alibabaCloudFileDO = aibabaCloudFileMapper.selectById(id);
        if (ObjectUtil.isNull(alibabaCloudFileDO)) {
            throw new BusinessException("文件id异常,请确认后重试");
        }
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode(alibabaCloudFileDO.getFileName(), "UTF-8");
            response.setContentType("application/x-download;charset=utf-8");
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
            //调用 阿里云下载
            File file = aliyunOssUtil.downLoadOssFile(alibabaCloudFileDO.getFileAddress(), fileName);
            byte[] array = FileUtils.readFileToByteArray(file);
            outputStream = response.getOutputStream();
            outputStream.write(array);
            outputStream.flush();
        } catch (IOException e) {
            log.error("源代码扫描文件下载失败,失败原因:", e);
        } finally {
            try {
                if (ObjectUtil.isNotNull(outputStream)) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return response;
    }

    /**
     * @param exportList:
     * @param fileName:
     * @param id:
     * @description 异步上传文件到 阿里云 OSS 该业务请求量很小 所以使用了 @Async 的异步方式
     */
    @Async
    public void uploadFileAliOss(List<SourceCodeAnalysisExportVO> exportList, String fileName, Long id) {
        File tempFile = null;
        AlibabaCloudFileDO alibabaCloudFileDO = new AlibabaCloudFileDO();
        alibabaCloudFileDO.setId(id);
        try {
            // 创建临时文件
            tempFile = File.createTempFile(fileName, ".xlsx");
            // 使用EasyExcel写入数据
            EasyExcel.write(tempFile, SourceCodeAnalysisExportVO.class)
                    .sheet("sheet1")
                    .doWrite(exportList);
            String fileUrl = aliyunOssUtil.uploadOssFile(tempFile, fileName + ".xlsx");
            alibabaCloudFileDO.setFileAddress(fileUrl);
            //更新文件生成成功
            alibabaCloudFileDO.setFileStatus(2);
        } catch (IOException e) {
            //文件生成失败
            alibabaCloudFileDO.setFileStatus(3);
            log.error("文件上传阿里云OSS 失败,文件导出主键id:{}", id, e);
        } finally {
            //更新
            aibabaCloudFileMapper.updateById(alibabaCloudFileDO);
            tempFile.delete();
        }
    }


}

下载文件代码

完成导出后,用户可以在页面上看到下载按钮,点击下载就可以完成导出的文件下载了。

java 复制代码
@PostMapping(value = "/download-source-code-analysis-file")
    @ApiOperation(httpMethod = "POST", value = "下载源代码分析文件", notes = "下载源代码分析文件")
    public HttpServletResponse downLoadSouceCodeAnalysisFile(@RequestParam("id") Long id, HttpServletResponse response) {
        return sourceCodeAnalysisService.downLoadSouceCodeAnalysisFile(id, response);
    }

总结:本篇重点是分享阿里云 OSS 文件上传下载功能,结合项目中的一个场景做了一个简单的异步导出,偏业务代码,简单分享,希望可以帮助到有需要的朋友。

如有不正确的地方欢迎各位指出纠正。

相关推荐
m0_7482540933 分钟前
2024.1.4版本的IntelliJ IDEA创建Spring Boot项目的详细步骤
java·spring boot·intellij-idea
程序员大金1 小时前
基于SpringBoot+Vue的驾校管理系统
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
m0_748239631 小时前
Java实战:Spring Boot application.yml配置文件详解
java·网络·spring boot
程序员大金1 小时前
基于SpringBoot+Vue的高校电动车租赁系统
前端·javascript·vue.js·spring boot·mysql·intellij-idea·旅游
qq8572226312 小时前
java+springboot+mysql科研成果管理系统
java·spring boot·mysql
m0_748252602 小时前
用Maven开发Spring Boot 项目
java·spring boot·maven
机智阳2 小时前
介绍一个InnoDB的数据页,和B+树的关系是什么?
java·数据结构·分布式·后端·b树
iiaythi2 小时前
spring boot - 接口返回枚举值描述
spring boot
啊松同学2 小时前
【Spring】使用@Async注解后导致的循环依赖问题
java·后端·spring
rock——you2 小时前
django通过关联表字段进行排序并去重
数据库·后端·postgresql·django