Spring Boot 集成阿里云OSS 完成文件上传下载
前言:
文件上传下载在项目开发中是一个非常常见的业务场景,在云服务上还没有兴起的时候,一般来说都会把文件单独存放到文件服务器上,随着云服务的兴起,各类云服务厂商都提供了 OSS 服务,本篇我们分享 Spring Boot 项目如何把文件存储到阿里云 OSS。
Spring Boot 集成阿里云 OSS
阿里云提供了 SDK,项目中引入相关依赖即可,我们在 pom.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 上传下载工具类,如下:
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) {// 关闭ossClientossClient.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.xlsxString 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) {// 关闭ossClientossClient.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) {// 关闭ossClientossClient.shutdown();}}}}
业务场景
业务场景要求用户端发起导出请求后,快速生成一个导出记录响应到用户端,后端异步完成导出操作,后端完成导出后,将导出的文件上传到阿里云 OSS,用户可以在页面完成文件的下载。
前面我们已经封装好了阿里云 OSS 的工具类,这里我们实现整个业务,调用 OSS 工具类完成文件的上传下载即可。
Service 代码如下
部分项目中的代码没有展示出来,了解整体实现思路即可。。
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 {@Autowiredprivate AibabaCloudFileMapper aibabaCloudFileMapper;@Autowiredprivate AliyunOssUtil aliyunOssUtil;//前面我们已经封装好了阿里云 OSS 的工具类,这里我们实现整个业务,调用 OSS 工具类完成文件的上传下载即可。@Transactional(rollbackFor = Exception.class)@Overridepublic 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());}@Overridepublic 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 的异步方式*/@Asyncpublic 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();}}}
下载文件代码
完成导出后,用户可以在页面上看到下载按钮,点击下载就可以完成导出的文件下载了。
@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 文件上传下载功能,结合项目中的一个场景做了一个简单的异步导出,偏业务代码,简单分享,希望可以帮助到有需要的朋友。
如有不正确的地方欢迎各位指出纠正。
相关文章:
Spring Boot 集成阿里云OSS 完成文件上传下载
前言: 文件上传下载在项目开发中是一个非常常见的业务场景,在云服务上还没有兴起的时候,一般来说都会把文件单独存放到文件服务器上,随着云服务的兴起,各类云服务厂商都提供了 OSS 服务,本篇我们分享 Spri…...

使用ERA5数据绘制风向玫瑰图的简易流程
使用ERA5数据绘制风向玫瑰图的简易流程 今天需要做一个2017年-2023年的平均风向的统计,做一个风向玫瑰图,想到的还是高分辨率的ERA5land的数据(0.1分辨率,逐小时分辨率,1950年至今)。 风向,我分为了16个&…...
测试脚本并发多进程:pytest-xdist用法
参考:https://www.cnblogs.com/poloyy/p/12694861.html pytest-xdist详解: https://www.cnblogs.com/poloyy/p/14708825.html 总 https://www.cnblogs.com/poloyy/category/1690628.html...

数据可视化的Python实现
一、GDELT介绍 GDELT ( www.gdeltproject.org ) 每时每刻监控着每个国家的几乎每个角落的 100 多种语言的新闻媒体 -- 印刷的、广播的和web 形式的,识别人员、位置、组织、数量、主题、数据源、情绪、报价、图片和每秒都在推动全球社会的事件,GDELT 为全…...

【Linux系列】Linux 系统配置文件详解:`/etc/profile`、`~/.bashrc` 和 `~/.bash_profile`
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

uni-app实现小程序、H5图片轮播预览、双指缩放、双击放大、单击还原、滑动切换功能
前言 这次的标题有点长,主要是想要表述的功能点有点多; 简单做一下需求描述 产品要求在商品详情页的头部轮播图部分,可以单击预览大图,同时在预览界面可以双指放大缩小图片并且可以移动查看图片,双击放大࿰…...

游戏引擎学习第45天
仓库: https://gitee.com/mrxiao_com/2d_game 回顾 我们刚刚开始研究运动方程,展示了如何处理当人物遇到障碍物时的情况。有一种版本是角色会从障碍物上反弹,而另一版本是角色会完全停下来。这种方式感觉不太自然,因为在游戏中,…...

electron常用方法
一,,electron设置去除顶部导航栏和menu 1,electron项目 在创建BrowserWindow实例的main.js页面添加frame:false属性 2,electron-vue项目 在src/main/index.js文件下找到创建窗口的方法(createWindow)&…...

【Spark】Spark Join类型及Join实现方式
如果觉得这篇文章对您有帮助,别忘了点赞、分享或关注哦!您的一点小小支持,不仅能帮助更多人找到有价值的内容,还能鼓励我持续分享更多精彩的技术文章。感谢您的支持,让我们一起在技术的世界中不断进步! Sp…...
meta llama 大模型一个基础语言模型的集合
LLaMA 是一个基础语言模型的集合,参数范围从 7B 到 65B。我们在数万亿个 Token 上训练我们的模型,并表明可以专门使用公开可用的数据集来训练最先进的模型,而无需诉诸专有的和无法访问的数据集。特别是,LLaMA-13B 在大多数基准测试…...

JAVA爬虫获取1688关键词接口
以下是使用Java爬虫获取1688关键词接口的详细步骤和示例代码: 一、获取API接口访问权限 要使用1688关键词接口,首先需要获取API的使用权限,并了解接口规范。以下是获取API接口的详细步骤: 注册账号:在1688平台注册一…...
操作系统——内存管理
1、什么是虚拟内存?它是如何实现的?虚拟内存与物理内存之间有什么关系? 虚拟内存是操作系统提供的一种内存管理机制,它使程序认为自己拥有连续的内存空间,但实际上内存可能被分散存储在物理内存和磁盘交换空间中。 虚…...
android studio 模拟器不能联网?
模拟器路径: C:\Users\Administrator\AppData\Local\Android\Sdk\emulator\emulator.exe.关闭所有AVD设备实例 导航至: C:\Users\userName\AppData\Local\Android\Sdk\emulator查看模拟器名称 AdministratorDESKTOP-6JB1OGC MINGW64 ~/AppData/Local/…...
CTF-WEB: 目录穿越与模板注入 [第一届国城杯 Ez_Gallery ] 赛后学习笔记
step1 验证码处存在逻辑漏洞,只要不申请刷新验证码就一直有效 字典爆破得到 admin:123456 step2 /info?file../../../proc/self/cmdline获得 python/app/app.py经尝试,读取存在的目录时会返回 A server error occurred. Please contact the administrator./info?file.…...

数据结构6.4——归并排序
基本思想: 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个…...
【html 常用MIME类型列表】
本表仅列出了常用的MIME类型,完整列表参考文档。 浏览器通常使用 MIME 类型(而不是文件扩展名)来确定如何处理 URL,因此 Web 服务器在响应头中添加正确的 MIME 类型非常重要。 如果配置不正确,浏览器可能会曲解文件内容…...

Linux之vim编辑器
vi编辑器是所有Unix及linux系统下标准的编辑器,类似于Windows系统下的记事本。很多软件默认使用vi作为他们编辑的接口。vim是进阶版的vi,vim可以视为一种程序编辑器。 前言: 1.文件准备 复制 /etc/passwd文件到自己的目录下(不…...

【工具介绍】可以批量查看LableMe标注的图像文件信息~
在图像处理和计算机视觉领域,LabelMe是一个广泛使用的图像标注工具,它帮助我们对图像中的物体进行精确的标注。但是,当标注完成后,我们常常需要一个工具来批量查看这些标注信息。 今天,我要介绍的这款exe程序…...
2024年山西省第十八届职业院校技能大赛 (高职组)“信息安全管理与评估”赛项规程
2024年山西省第十八届职业院校技能大赛 (高职组)“信息安全管理与评估”赛项规程 一、赛项名称 赛项名称:信息安全管理与评估 英文名称:Information Security Management and Evaluation 赛项组别:高职教师组 赛项归属…...

STM32完全学习——STemWin的移植小插曲
一、移植编译的一些问题 新版的STemWin的库没有区别编译器,只有一些这样的文件,默认你将这些文件导入到KEIL中,然后编译就会有下面的错误。 ..\MEWIN\STemWin\Lib\STemWin_CM4_wc16.a(1): error: A1167E: Invalid line start ..\MEWIN\STe…...

Nginx--手写脚本压缩和切分日志(也适用于docker)
原文网址:Nginx--手写脚本压缩和切分日志(也适用于docker)_IT利刃出鞘的博客-CSDN博客 简介 本文介绍nginx如何手写脚本压缩和切分日志。 1.创建切分日志的脚本 创建脚本文件:/work/tmp/nginx-log_sh(后边要用run-…...
[SC]SystemC在CPU/GPU验证中的应用(三)
SystemC在CPU/GPU验证中的应用(三) 摘要:下面分享50个逐步升级SystemC编程能力的示例及建议的学习路线图。您可以一次一批地完成它们——从前五个基础的例子开始,然后转向channels, TLM, bus models, simple CPU/GPU kernels等等。在每个阶段掌握之后,再进行下一组…...

AI书签管理工具开发全记录(八):Ai创建书签功能实现
文章目录 AI书签管理工具开发全记录(八):AI智能创建书签功能深度解析前言 📝1. AI功能设计思路 🧠1.1 传统书签创建的痛点1.2 AI解决方案设计 2. 后端API实现 ⚙️2.1 新增url相关工具方法2.1 创建后端api2.2 创建crea…...
随笔笔记记录5.28
1.setOptMode -opt_leakage_to_dynamic_ratio 调整漏电与动态功耗的优化权重( 1.0 表示仅优化漏电)。 需指定-opt_power_effort(none | low | high),同时使用 2.set_ccopt_property max_source_to_sink_net_length …...
Linux 下如何查看进程的资源限制信息?
简介 Linux 上的 cat /proc/$pid/limits 命令提供有关特定进程的资源限制的信息,其中 $pid 是相关进程的进程 ID (pid)。该文件是 /proc 文件系统的一部分,该文件系统是一个虚拟文件系统,提供有关进程和系统资源的信息…...
ArcGIS Pro 3.4 二次开发 - 地理处理
环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 地理处理1 通用1.1 如何执行模型工具1.2 设置地理处理范围环境1.3 在 Geoprocessing 窗格中打开脚本工具对话框1.4 打开特定工具的地理处理工具窗格1.5 获取地理处理项目项1.6 阻止通过GP创建的特征类自动添加到地图中1.7 GPExecut…...

微深节能 码头装卸船机定位与控制系统 格雷母线
微深节能码头装卸船机定位与控制系统:格雷母线技术赋能港口作业智能化升级 在现代化港口散货装卸作业中,装卸船机是连接船舶与陆域运输的核心枢纽设备。传统装卸船机依赖人工操作,存在定位偏差大、动态协同难、安全风险高等痛点。微深节能基于…...

技术分享 | Oracle SQL优化案例一则
本文为墨天轮数据库管理服务团队第70期技术分享,内容原创,作者为技术顾问马奕璇,如需转载请联系小墨(VX:modb666)并注明来源。 一、问题概述 开发人员反映有条跑批语句在测试环境执行了很久都没结束&…...

外网访问可视化工具 Grafana (Linux版本)
Grafana 是一款强大的可视化监控指标的展示工具,可以将不同的数据源数据以图形化的方式展示,不仅通用而且非常美观。它支持多种数据源,如 prometheus 等,也可以通过插件和 API 进行扩展以满足各种需求。 本文将详细介绍如何在本地…...
N2语法 状態
1,~てならない 接続:て型 意味:…得不得了(强调自然产生的情感,可接自发动词) 例文: お腹が痛くてならない。 心配でならない。 両親に会いたくてならない。(非常…...