1.文件上传

- 前端 和 服务器端

- 想要上传文件,前端要求


- 代码使用示例(单文件上传接口)
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
public class FileUploadController {
// 处理单文件上传
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
// 判断文件是否为空
if (file.isEmpty()) {
return "请选择要上传的文件";
}
try {
// 获取原始文件名
String fileName = file.getOriginalFilename();
// 定义文件保存路径(例如:项目根目录下的 upload 文件夹)
String filePath = "D:/upload/";
File dest = new File(filePath + fileName);
// 确保保存目录存在(不存在则创建)
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
// 保存文件到本地
file.transferTo(dest);
return "文件上传成功,保存路径:" + dest.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
return "文件上传失败:" + e.getMessage();
}
}
}
关键说明
接口参数:通过 @RequestParam("file") 绑定前端上传的文件,其中 file 需与前端表单中 input 的 name 属性值一致(如 <input type="file" name="file">)。
- 多文件上传:若需同时上传多个文件,可使用 List 接收
java
@PostMapping("/upload-multiple")
public String uploadMultipleFiles(@RequestParam("files") List<MultipartFile> files) {
// 遍历文件列表,逐个处理
for (MultipartFile file : files) {
// 处理逻辑同单文件上传...
}
return "多文件上传完成";
}

2.文件存储到本地
file.transferTo() 就是把用户上传的临时文件 "转移" 并永久保存到你指定的本地位置的关键操作。
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@RestController
public class UploadController {
private static final Logger log = LoggerFactory.getLogger(UploadController.class);
// 定义文件保存的根目录(实际项目中建议配置在application.properties中,方便修改)
private static final String UPLOAD_BASE_DIR = "D:/images/";
// 允许上传的文件类型(根据业务需求调整)
private static final String[] ALLOWED_CONTENT_TYPES = {
"image/jpeg", "image/png", "image/gif", "image/bmp"
};
// 单个文件最大大小限制(5MB,1MB=1024*1024字节)
private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;
@PostMapping("/upload")
public ResponseEntity<String> upload(
@RequestParam String name,
@RequestParam Integer age,
@RequestParam MultipartFile file) {
// 1. 校验文件是否为空
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("上传失败:请选择文件");
}
try {
// 2. 校验文件大小
if (file.getSize() > MAX_FILE_SIZE) {
return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
.body("上传失败:文件大小不能超过5MB");
}
// 3. 校验文件类型(通过MIME类型判断)
String contentType = file.getContentType();
boolean isAllowed = false;
for (String allowedType : ALLOWED_CONTENT_TYPES) {
if (allowedType.equals(contentType)) {
isAllowed = true;
break;
}
}
if (!isAllowed) {
return ResponseEntity.badRequest()
.body("上传失败:仅支持JPG、PNG、GIF、BMP格式的图片");
}
// 4. 确保保存目录存在(不存在则创建,包括多级目录)
File baseDir = new File(UPLOAD_BASE_DIR);
if (!baseDir.exists()) {
boolean mkdirsSuccess = baseDir.mkdirs();
if (!mkdirsSuccess) {
log.error("创建上传目录失败:{}", UPLOAD_BASE_DIR);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("上传失败:服务器存储目录创建失败");
}
}
// 5. 生成唯一文件名(避免重名覆盖)
// 5.1 获取原始文件名的后缀(如.jpg)
String originalFilename = file.getOriginalFilename();
String fileSuffix = "";
if (originalFilename.contains(".")) {
fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."));
}
// 5.2 生成唯一文件名:UUID + 时间戳 + 后缀(确保唯一性)
String uniqueFileName = UUID.randomUUID().toString()
+ "_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
+ fileSuffix;
// 6. 构建目标文件路径
File destFile = new File(baseDir, uniqueFileName);
// 7. 保存文件到本地
file.transferTo(destFile);
log.info("文件上传成功:{} -> {}", originalFilename, destFile.getAbsolutePath());
// 8. 返回成功响应(包含实际保存的文件名)
return ResponseEntity.ok("文件上传成功,保存路径:" + destFile.getAbsolutePath());
} catch (IOException e) {
log.error("文件上传失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("上传失败:" + e.getMessage());
}
}
}

2.文件存储在阿里云OSS
- 存在服务器的本地磁盘,无法无限增大存储容量,磁盘容量终有限
- 生产环境中常采用云服务



- 使用阿里云OSS -少量改动,直接默认选项后确认
- 创建Bucket桶


- 更改权限


- 获取秘钥





java
set OSS_ACCESS_KEY_ID=xxxxxxxxxx
set OSS_ACCESS_KEY_SECRET=xxxxxxxxxxxxxxx
setx OSS_ACCESS_KEY_ID "%OSS_ACCESS_KEY_ID%"
setx OSS_ACCESS_KEY_SECRET "%OSS_ACCESS_KEY_SECRET%"
echo %OSS_ACCESS_KEY_ID%
echo %OSS_ACCESS_KEY_SECRET%

- 泄露OSS_ACCESS_KEY之后,直接禁用重新创建即可

3.参照官方SDK编写入门程序



- 引入依赖
java
<!-- 引入阿里云OSS依赖-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
- 这个前面已经配置过了

- 复制文件上次的demo进行配置

java
package com.itheima;
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import java.io.File;
public class Demo {
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "tianwen-java-ai";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String objectName = "Snipaste_2025-11-08_09-27-06.png";
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
String region = "cn-shenzhen";
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
String filePath= "C:\\Users\\59742\\Desktop\\Snipaste_2025-11-08_09-27-06.png";
// 创建OSSClient实例。
// 当OSSClient实例不再使用时,调用shutdown方法以释放资源。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new File(filePath));
// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
// 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();
}
}
}
}
-
Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";


-
填写Bucket名称,例如examplebucket。
String bucketName = "tianwen-java-ai";

-
填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String objectName = "Snipaste_2025-11-08_09-27-06.png";
-
填写本地文件的完整路径,例如D:\localpath\examplefile.txt。
如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
String filePath= "C:\Users\59742\Desktop\Snipaste_2025-11-08_09-27-06.png";
3.运行代码
- 代码配置完成之后,需要重启IDEA,否则可能IDEA可能读取不到刚刚CMD里配置的已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
- 运行Demo程序,上传成功!

4.阿里云OSS,案例集成


- 代码案例

- UploadController类
java
package com.itheima.controller;
import com.itheima.pojo.Result;
import com.itheima.utils.AliyunOSS0perator;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@Slf4j
@RestController
public class UploadController {
private static final Logger log = LoggerFactory.getLogger(UploadController.class);
@Autowired
private AliyunOSS0perator aliyunOSS0perator;
@RequestMapping("/upload")
public Result upload(MultipartFile file) throws Exception {
log.info("文件上传开始...{}", file.getOriginalFilename());
// 将文件交给OSS存储管理
String url = aliyunOSS0perator.upload(file.getBytes(), file.getOriginalFilename());
log.info("文件的URL:{}", url);
return Result.success(url);
}
}
- AliyunOSSOperator类
java
package com.itheima.utils;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyuncs.exceptions.ClientException;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
@Component
public class AliyunOSS0perator {
private String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";
private String bucketName = "tianwen-java-ai";
private String region = "cn-shenzhen";
public String upload(byte[] bytes, String originalFilename) throws ClientException {
// 从环境变量中获取访问凭证,运行代码前,确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 构造文件存储路径(按年月分目录 + 随机UUID文件名)
String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
// 生成不会重复的新文件名
String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
String objectName = dir + "/" + newFileName;
// 创建OSSClient实例
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// 上传文件到OSS
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
// 生成可访问的URL(这里以公共读权限为例,实际可根据业务调整)
return endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + objectName; } finally {
// 关闭OSSClient
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}

3.1 代码优化

- 这三个信息需要变动,不建议硬编码,将需要变动的信息配置在yml文件中

java
通过直接注入的方式,不用写死在代码中
public class AliyunOSS0perator {
@Value("${aliyun.endpoint}")
private String endpoint;
@Value("${aliyun.bucketName}")
private String bucketName;
@Value("${aliyun.region}")
private String region;
...
...
...
}
application.yml文件中↓
# aliyun_OSS相关配置
aliyun:
endpoint: https://oss-cn-shenzhen.aliyuncs.com
bucketName: tianwen-java-ai
region: cn-shenzhen
4.2 @Value注入配置文件,过于繁琐,每次都要手动@Value每一个属性

- 简化@Value注解带来的问题
- 将配置项数据配置到实体类中,使用时,直接@Autowire注入Bean即可

java
@Data
@Component
// 定义配置项映射 prefix是前缀
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
private String endpoint;
private String bucketName;
private String region;
}
# aliyun_OSS相关配置
aliyun:
oss:
endpoint: https://oss-cn-shenzhen.aliyuncs.com
bucketName: tianwen-java-ai
region: cn-shenzhen
@Component
public class AliyunOSS0perator {
@Autowired
AliyunOSSProperties aliyunOSSProperties;
public String upload(byte[] bytes, String originalFilename) throws ClientException {
String endpoint = aliyunOSSProperties.getEndpoint();
String bucketName = aliyunOSSProperties.getBucketName();
String region = aliyunOSSProperties.getRegion();
...
...
...
}
