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 文件上传下载功能,结合项目中的一个场景做了一个简单的异步导出,偏业务代码,简单分享,希望可以帮助到有需要的朋友。

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

相关推荐
why1514 小时前
腾讯(QQ浏览器)后端开发
开发语言·后端·golang
浪裡遊4 小时前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
声声codeGrandMaster4 小时前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
呼Lu噜5 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_1585 小时前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩5 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04125 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝5 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel5 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581366 小时前
什么是MCP
后端·程序员