5.【SpringBoot3】文件上传
1. 文件上传到本地
需求分析
在用户更换头像或发布文章时,需要携带一个图片的 url 地址,该 url 地址是当用户访问文件上传接口,将图片上传成功后,服务器返回的地址。所以,后台需要提供一个文件上传接口,用来接收前端提交的文件数据,并且返回文件的访问地址。

接口文档

文件上传知识回忆
前端页面文件上传三要素:
- 请求方式必须是 post;
- 表单的编码类型必须是 multipart/form-data;
- 文件表单项对应的类型必须是 file。

这三个要素满足后,用户就可以选择要上传的文件,点击提交按钮,最终把文件内容提交给服务器。当服务器接收到该请求后,如果服务器中的代码是使用 SpringMVC 框架编写的,我们就可以在方法上声明一个 MultipartFile 类型的参数,用来接收上传的文件内容。MultipartFile 提供了很多方法:

接口实现思路
Controller 中声明用于上传文件的 upload()方法,方法内部需要借助 MultipartFile 提供的 api,把文件内容写入本地磁盘文件,因此方法上要声明一个 MultipartFile 类型的参数。

现在还没有把文件内容上传到阿里云服务器,我们先把文件存储到服务器本地,保证接口访问是正常的,接口没问题后,再把文件上传到服务器测试。
@RestController
public class FileUploadController {@PostMapping("/upload")public Result<String> upload(MultipartFile file) throws IOException {//把文件内容存储到本地磁盘//获取文件原始的名称String originalFilename = file.getOriginalFilename();//file.transferTo的异常直接抛出去//再次存储的时候使用原来的文件名file.transferTo(new File("C:\\Users\\xxx\\Desktop\\files\\"+originalFilename));return Result.success("url访问地址...");}
}
postman 测试:

点击 select file 选择要上传的文件:

打开文件:

上传成功:


但是,现在的代码中还存在bug。当我们在 postman 中针对刚刚选择的图片再次点击 send 时,files 文件夹中并没有出现两张图片。原因是后上传的图片与之前上传的图片重名,导致之前的图片被覆盖了。在现实中,很有可能出现需要上传两张同名图片的需求,比如两个用户上传的图片同名。因此这个问题需要解决一下。
解决方式就是保证上传时的文件名唯一:在文件后缀名前面拼接UUID:

再次上传:

现在只是通过将文件上传到本地的方式模拟了将文件上传到服务器的过程。下面来介绍将文件上传到服务器的实现方式。
2. 文件上传到云服务器
云服务器会提供很多服务,当计算机连接上云服务器并开通某个服务后后就可以使用该服务了。

在本节,我们使用阿里云的对象存储服务。
使用云对象存储 OSS(Object Storage Service),可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。
使用第三方服务的通用思路
- 准备工作:在服务提供商网站上注册用户,开通对应服务
- 参照官方 SDK 编写入门程序:引入阿里云 OSS 相关的 jar 包,并参照提供的示例代码编写程序
- 参照入门程序,将功能集成到自己的代码中使用
SDK:Software Development Kit
的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等,都可以叫做SDK。
2.1 准备工作
在服务提供商网站上注册用户,开通对应服务

① 登录之后,点击控制台

② 查看阿里云提供的所有服务


③ 搜索对象存储 OSS

④ 首次使用 OSS 需要开通

⑤ 按照提示开通后,开始创建 bucket

⑥ 填写必要信息,创建 bucket

⑦ 创建成功后,进入 bucket

⑧ 概览中展示了 bucket 基本信息和访问地址

⑨ 获取 AccessKey:




根据提示一步一步操作就能获取到 AccessKey。至此,准备工作完成。
2.2 参照官方 SDK 编写入门程序
导入阿里云 OSS 相关的依赖坐标,并参照提供的示例代码编写程序。
(1) 导入 OSS 相关依赖坐标
① 左侧点击概览:

② 滑动到底部,点击 SDK 下载

③ 找到 Java 版本,点击查看文档,会弹出帮助文档。点击“文档中心打开”

④ 点击”安装“

⑤ 将所需依赖坐标导入我们的 pom.xml 文件:

(2) 参照提供的示例代码编写程序
① 因为是要上传文件,所以点击:对象/文件➡上传文件➡简单上传

② 此处给出了一个示例代码,copy 一下

copy 的示例代码如下:
public class Demo {public static void main(String[] args) throws Exception {// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();// 填写Bucket名称,例如examplebucket。String bucketName = "examplebucket";// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。String objectName = "exampledir/exampleobject.txt";// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);try {// 填写字符串。String content = "Hello OSS,你好世界";// 创建PutObjectRequest对象。PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。// ObjectMetadata metadata = new ObjectMetadata();// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());// metadata.setObjectAcl(CannedAccessControlList.Private);// putObjectRequest.setMetadata(metadata);// 上传字符串。PutObjectResult result = ossClient.putObject(putObjectRequest); } catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}}
}
③ 修改示例代码
public class Demo {@Testpublic void test() throws Exception {// Endpoint:bucket概览中的访问端口->外网访问的地域节点String endpoint = "https://oss-cn-beijing.aliyuncs.com";// 阿里云为了防止AccessKeyId和AccessKeySecret泄露,建议以配置环境变量的方式来使用// 这里是测试代码,就不再配置环境变量了,直接定义两个变量来模拟,值都改成自己的String ACCESS_KEY_ID="**********";String ACCESS_KEY_SECRET="**********";// Bucket名称String bucketName = "big-event-bucket1";// 要存储的对象的名称,这里的对象可以是字符串、图片、视频等String objectName = "cat.jpg";// 创建OSSClient实例OSS ossClient = new OSSClientBuilder().build(endpoint, ACCESS_KEY_ID, ACCESS_KEY_SECRET);try {// 借助图片输入流对象构建出PutObjectRequest对象PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new FileInputStream("C:\\Users\\xxx\\Desktop\\cat.jpg"));// 上传到服务器PutObjectResult result = ossClient.putObject(putObjectRequest);} catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}}
}
(3) 运行代码
上传成功:

点击图片名称,弹出图片具体信息。复制图片在服务器上的 url。

在浏览器中输入图片在服务器上的 url,搜索。此时已从服务器上下载了图片。

虽然上面相对于阿里云提供的示例代码该类很多,但只要完成这个 Demo 的改造,今后在一般情况下需要改的地方就只有下面两处:要存储的对象的名称、输入流中要存储的对象在本地的位置。

2.3 案例集成 OSS
参照入门程序,将功能集成到自己的代码中使用。
为了使用方便,需要把上面改造成功的代码封装成一个工具类。将来哪里需要使用,直接调用工具类的方法即可。
public class AliOssUtil {//这四个一般不变,所以抽取到成员处,声明为静态常量public static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";//区域节点public static final String ACCESS_KEY_ID="LTAI5t9NJbszDF431vYUkzbh";public static final String ACCESS_KEY_SECRET="d82arCTGqH3Turz4HY3NA4a9RufHn0";public static final String BUCKET_NAME = "big-event-bucket1";// Bucket名称// objectName: 要存储的对象的名称,这里的对象可以是字符串、图片、视频等// 方法内部可能发生变化的是objectName和输入流,因此以参数的方式暴露出去// 方法要返回图片在服务器上的访问地址,因此返回值是String类型public static String upLoadFile(String objectName, InputStream inputStream) throws Exception {// 创建OSSClient实例OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);String url = ""; //为了返回url,先声明一个空字符串try {PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, objectName, inputStream);PutObjectResult result = ossClient.putObject(putObjectRequest);//上传到服务器//如果下面的代码不报错,就该给url赋值了//url组成:https://bucket名称.区域节点(去掉https://)/objectName//ENDPOINT.lastIndexOf("/")+1: 在ENDPOINT中,从最后一个"/"的下一个位置开始截取字符串url="https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.lastIndexOf("/")+1)+"/"+objectName;} catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}return url;}
}
现在工具类准备好了,以后需要将文件上传阿里云只需调用 upLoadFile() 方法即可。
在 FileUploadController 中,之前我们将文件存储到了本地磁盘,现在不需要了,直接调用 upLoadFile() 方法将文件上传服务器。upLoadFile() 方法需要传递两个参数,filename 可以作为传入的第一个参数 objectName;第二个参数是文件输入流,MultipartFile 提供的 getInputStream() 方法可以提供该参数。最后将 upLoadFile() 方法返回的 url 响应给浏览器。
@RestController
public class FileUploadController {@PostMapping("/upload")public Result<String> upload(MultipartFile file) throws Exception {//把文件内容上传到阿里云服务器//获取原始文件名String originalFilename = file.getOriginalFilename();//使用UUID生成文件名,保证文件名唯一,从而防止文件被覆盖//originalFilename.lastIndexOf("."): 从文件名的最后一个"."处开始截取字符串(包括"."),即获取原始文件后缀名String filename = UUID.randomUUID().toString()+originalFilename.substring(originalFilename.lastIndexOf("."));// file.transferTo(new File("C:\\Users\\xxx\\Desktop\\files\\"+filename));//upLoadFile()方法上的异常直接抛出去即可String url = AliOssUtil.upLoadFile(filename, file.getInputStream());return Result.success(url);}
}
postman 测试:

在浏览器中输入文件访问地址,可以下载:

相关文章:
5.【SpringBoot3】文件上传
1. 文件上传到本地 需求分析 在用户更换头像或发布文章时,需要携带一个图片的 url 地址,该 url 地址是当用户访问文件上传接口,将图片上传成功后,服务器返回的地址。所以,后台需要提供一个文件上传接口,用…...
网络安全态势感知平台概述
网络安全态势感知平台 文章目录 网络安全态势感知平台网络安全态势感知平台是什么一、网络安全态势感知平台是什么?二、网络安全态势感知很重要三、网络安全态势感知基础功能以某公司态势平台产品为例具体功能有以下: 网络安全态势感知平台是什么 网络安…...
PHP导出csv文件格式(最快捷的方式导出Excel文件)
php导出csv文件格式比起用PHPExcel插件导出excel文件速度快100倍! 以下是几种不同的PHP导出CSV文件的方法: 方法一(php://output方式用fputcsv函数格式化成csv数据): ----------------------------------------------…...
vue3常用代码
文章目录 监听路由vue3 警告Feature flag __VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.mitt、project/inject 无效解决方案 菜鸟做项目时发现很多 vue3 常用的代码,所以来总结一下! 监听路由 import { useRoute } from "…...
【技术】SpringBoot 接口怎么加密解密
1. 介绍 在我们日常的Java开发中,免不了和其他系统的业务交互,或者微服务之间的接口调用 如果我们想保证数据传输的安全,对接口出参加密,入参解密。 但是不想写重复代码,我们可以提供一个通用starter,提…...
SqlAlchemy使用教程(六) -- ORM 表间关系的定义与CRUD操作
SqlAlchemy使用教程(一) 原理与环境搭建SqlAlchemy使用教程(二) 入门示例及编程步骤SqlAlchemy使用教程(三) CoreAPI访问与操作数据库详解SqlAlchemy使用教程(四) MetaData 与 SQL Express Language 的使用SqlAlchemy使用教程(五) ORM API 编程入门 本章内容,稍微有…...
嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM平台编程第五天-kernel配置(物联技术666)
链接:https://pan.baidu.com/s/1eb94AaDM-cIZsbr929Isbw?pwd1688 提取码:1688 上午:linux内核介绍 徐登伟老师 下午:linux的配置 教学内容: 一、基本kernel的制作: 1、去开源社区下载原…...
Java笔记(死锁、线程通信、单例模式)
一、死锁 1.概述 死锁 : 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法往下执行。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进…...
DAY11_(简易版)VUEElement综合案例
目录 1 VUE1.1 概述1.1.1 Vue js文件下载 1.2 快速入门1.3 Vue 指令1.3.1 v-bind & v-model 指令1.3.2 v-on 指令1.3.3 条件判断指令1.3.4 v-for 指令 1.4 生命周期1.5 案例1.5.1 需求1.5.2 查询所有功能1.5.3 添加功能 2 Element2.0 element-ui js和css和字体图标下载2.1 …...
【Kafka】开发实战和Springboot集成kafka
目录 消息的发送与接收生产者消费者 SpringBoot 集成kafka服务端参数配置 消息的发送与接收 生产者 生产者主要的对象有: KafkaProducer , ProducerRecord 。 其中 KafkaProducer 是用于发送消息的类, ProducerRecord 类用于封装Kafka的消息…...
【C语言】(1)初识C语言
什么是C语言 C语言是一种广泛应用的计算机编程语言,它具有强大的功能和灵活性,使其成为系统编程和底层开发的首选语言。C语言的设计简洁、高效,且不依赖于特定的硬件或系统,因此在各种计算平台上都能稳定运行。 C语言的特点 高…...
SpringCloudStream整合MQ(待完善)
概念 Spring Cloud Stream 的主要目标是各种各样MQ的学习成本,提供一致性的编程模型,使得开发者能够更容易地集成消息组件(如 Apache Kafka、RabbitMQ、RocketMQ) 官网地址:Spring Cloud Stream 组件 1. Binder 2…...
【Java 数据结构】包装类简单认识泛型
包装类&简单认识泛型 1 包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和自动拆箱 2 什么是泛型3 引出泛型3.1 语法 4 泛型类的使用4.1 语法4.2 示例4.3 类型推导(Type Inference) 5 泛型如何编译的5.1 擦除机制5.2 为什么不能实例化泛型类型数组 6 泛型…...
第139期 做大还是做小-Oracle名称哪些事(20240125)
数据库管理139期 2024-01-25 第139期 做大还是做小-Oracle名称哪些事(20240125)1 问题2 排查3 扩展总结 第139期 做大还是做小-Oracle名称哪些事(20240125) 作者:胖头鱼的鱼缸(尹海文) Oracle A…...
驱动开发--多路复用-信号
一、多路复用 每个进程都有一个描述符数组,这个数组的下标为描述符, 描述符的分类: 文件描述符:设备文件、管道文件 socket描述符 1.1 应用层:三套接口select、poll、epoll select:位运算实现 监控的描…...
LeetCode 2859. 计算 K 置位下标对应元素的和【位操作】1000
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...
composer安装hyperf后,nginx配置hyperf
背景 引入hyperf项目用作微服务,使用composer 安装hyperf后,对hyperf进行nginx配置。 配置步骤 因为hyperf监听的是端口,不像其他laravel、lumen直接指向文件即可。所有要监听端口号。 1 配置nginx server {listen 80;//http:…...
Flink对接Kafka的topic数据消费offset设置参数
scan.startup.mode 是 Flink 中用于设置消费 Kafka topic 数据的起始 offset 的配置参数之一。 scan.startup.mode 可以设置为以下几种模式: earliest-offset:从最早的 offset 开始消费数据。latest-offset:从最新的 offset 开始消费数据。…...
TryHackMe-Umbrella
靶场介绍 Breach Umbrella Corp’s time-tracking server by exploiting misconfigurations around containerisation. 利用集装箱化的错误配置,破坏Umbrella公司的时间跟踪服务器。 Task 1 What is the DB password? 数据库的密码是多少? 端口扫描&am…...
Excel导出警告:文件格式和拓展名不匹配
原因描述: Content-Type 原因:Content-Type,即内容类型,一般是指网页中存在的Content-Type,用于定义网络文件的类型和网页的编码,决定文件接收方将以什么形式、什么编码读取这个文件,这就是经常…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
