Spring Boot項(xiàng)目中實(shí)現(xiàn)文件上傳功能的示例
在實(shí)際項(xiàng)目中,文件上傳是很多項(xiàng)目必不可少的一個(gè)功能。那么在 Spring Boot 項(xiàng)目中又是如何來(lái)實(shí)現(xiàn)文件上傳功能的呢?一般來(lái)說(shuō),上傳的文件可以保存到項(xiàng)目根目錄下的某一文件夾中,但這樣做顯然是不太合適的。因此我們選擇將文件上傳到專門的文件服務(wù)器中。很多云計(jì)算廠商都提供文件存儲(chǔ)服務(wù)。這里我選擇的是阿里云的對(duì)象存儲(chǔ)(OSS)。
一、配置OSS
1. 導(dǎo)入SDK
首先,你需要注冊(cè)阿里云的賬號(hào)并開通對(duì)象存儲(chǔ)服務(wù)。在準(zhǔn)備工作完成之后,需要導(dǎo)入 JAVA 版本的 SDK,這里使用 maven 進(jìn)行導(dǎo)入
<!-- 阿里云OSS對(duì)象存儲(chǔ) --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.8.0</version> </dependency>
2. 修改配置文件
導(dǎo)入完成后在 application.properties 配置文件中添加以下內(nèi)容
# 節(jié)點(diǎn)域名 aliyun.oss.endpoint=oss-cn-xxxxxxx.aliyuncs.com # 賬戶id aliyun.oss.accessKeyId=xxxxxxxxxxxxx # 賬戶密碼 aliyun.oss.accessKeySecret=xxxxxxxxxxxxx # bucket名稱 aliyun.oss.bucketName=xxxxxxxxxxx # 簽名過(guò)期時(shí)間 aliyun.oss.policy.expire=300 # 上傳文件的最大尺寸 aliyun.oss.maxSize=10 # 上傳地址的前綴 aliyun.oss.dir.prefix=xxx # 回調(diào)參數(shù)的請(qǐng)求地址 aliyun.oss.callback=http://www.xxxxxx.com/api/aliyun/oss/callback
以上內(nèi)容在開通服務(wù)后均可獲取到,請(qǐng)根據(jù)實(shí)際情況進(jìn)行修改
3. 初始化
OSSClient是OSS的Java客戶端,用于管理存儲(chǔ)空間和文件等OSS資源。使用Java SDK發(fā)起OSS請(qǐng)求,您需要初始化一個(gè)OSSClient實(shí)例,并根據(jù)需要修改ClientConfiguration的默認(rèn)配置項(xiàng)。
根據(jù)官方文檔的描述,需要初始化一個(gè)ossClient實(shí)例并將其注入到Spring容器中,因此可以編寫一個(gè)配置類OssConfig
@Configuration
@PropertySource(value = {"classpath:application.properties"}, encoding = "utf-8")
public class OssConfig {
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String secretAccessKey;
@Bean
public OSS ossClient(){
return new OSSClientBuilder().build(endpoint, accessKeyId, secretAccessKey);
}
}
更多詳細(xì)的配置,請(qǐng)參考官方文檔:初始化
二、文件上傳
1. 流程分析
我們以典型的表單上傳為例,在使用對(duì)象存儲(chǔ)OSS后,表單上傳分為以下幾個(gè)流程:

注:Policy表單域用于驗(yàn)證請(qǐng)求的合法性。例如可以指定上傳的大小,可以指定上傳的Object名稱等,上傳成功后客戶端跳轉(zhuǎn)到的URL,上傳成功后客戶端收到的狀態(tài)碼。
PolicyConditions policyConds = new PolicyConditions(); policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize); policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, DIR_PREFIX); String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8); // 將Policy字符串進(jìn)行base64編碼 String policy = BinaryUtil.toBase64String(binaryData); // 用OSS的AccessKeySecret對(duì)base64編碼后的Policy進(jìn)行簽名 String signature = ossClient.calculatePostSignature(postPolicy);
前端向OSS服務(wù)器上傳文件時(shí)要上傳Policy表單域,OSS服務(wù)器將對(duì)Policy表單域的內(nèi)容進(jìn)行驗(yàn)證。關(guān)于 Post Policy 的詳細(xì)內(nèi)容,請(qǐng)參考官方文檔:Post Policy
當(dāng)文件上傳成功后,OSS服務(wù)器會(huì)向應(yīng)用服務(wù)器發(fā)起回調(diào)請(qǐng)求,具體流程如下:

用戶只需要在發(fā)送給 OSS 的請(qǐng)求中攜帶相應(yīng)的 Callback 參數(shù),即能實(shí)現(xiàn)回調(diào)。
Callback 參數(shù)是由一段經(jīng)過(guò) base64 編碼的 JSON 字符串(字段)。構(gòu)建 callback 參數(shù)的關(guān)鍵是指定請(qǐng)求回調(diào)的服務(wù)器 URL(callbackUrl)以及回調(diào)的內(nèi)容(callbackBody)。
// 上傳回調(diào)參數(shù)
Callback callback = new Callback();
// 指定請(qǐng)求回調(diào)的服務(wù)器URL
callback.setCallbackUrl(CALLBACK);
//(可選)設(shè)置回調(diào)請(qǐng)求消息頭中Host的值,即您的服務(wù)器配置Host的值。
// callback.setCallbackHost("yourCallbackHost");
// 設(shè)置發(fā)起回調(diào)時(shí)請(qǐng)求body的值。
callback.setCallbackBody("{\\\"filename\\\":${object},\\\"mineType\\\":${mimeType}}");
// 設(shè)置發(fā)起回調(diào)請(qǐng)求的Content-Type。
callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
// 設(shè)置發(fā)起回調(diào)請(qǐng)求的自定義參數(shù),由Key和Value組成,Key必須以x:開始。
// callback.addCallbackVar("x:dir", "value");
更詳細(xì)的內(nèi)容請(qǐng)閱讀官方文檔:Callback
2. 功能實(shí)現(xiàn)
首先編寫 Post Policy 封裝對(duì)象OssPolicyResult
@Data
public class OssPolicyResult {
@ApiModelProperty("用戶id")
private String accessKeyId;
@ApiModelProperty("Post Policy經(jīng)過(guò)base64編碼過(guò)的字符串")
private String policy;
@ApiModelProperty("對(duì)policy簽名后的字符串")
private String signature;
// @ApiModelProperty("對(duì)象的鍵值")
// private String key;
@ApiModelProperty("上傳文件夾路徑前綴")
private String dir;
@ApiModelProperty("oss對(duì)外服務(wù)的訪問(wèn)域名")
private String host;
@ApiModelProperty("上傳成功后的回調(diào)設(shè)置")
private String callback;
}
然后需自定義一個(gè)回調(diào)結(jié)果對(duì)象OssCallBackResult
@Data
public class OssCallBackResult {
@ApiModelProperty("文件的鏈接")
private String url;
@ApiModelProperty("文件名稱")
private String filename;
@ApiModelProperty("文件大小")
private String size;
@ApiModelProperty("文件的mimeType")
private String mimeType;
@ApiModelProperty("圖片文件的寬")
private String width;
@ApiModelProperty("圖片文件的高")
private String height;
}
注:以上內(nèi)容可根據(jù)實(shí)際需要進(jìn)行修改
之后編寫 Service 接口及實(shí)現(xiàn)類
Service 接口:
public interface OssService {
// 生成Post Policy
OssPolicyResult policy();
// 上傳成功后的回調(diào)
OssCallBackResult callback(Map<String, Object> requestBody);
}
Service 實(shí)現(xiàn)類:
@Slf4j
@Service
@PropertySource(value = {"classpath:application.properties"}, encoding = "utf-8")
public class OssServiceImpl implements OssService {
@Value("${aliyun.oss.endpoint}")
private String ENDPOINT;
@Value("${aliyun.oss.accessKeyId}")
private String ACCESS_KEY_ID;
@Value("${aliyun.oss.accessKeySecret}")
private String SECRET_ACCESS_KEY;
@Value("${aliyun.oss.bucketName}")
private String BUCKET_NAME;
@Value("${aliyun.oss.policy.expire}")
private int EXPIRE;
@Value("${aliyun.oss.maxSize}")
private int MAX_SIZE;
@Value("${aliyun.oss.dir.prefix}")
private String DIR_PREFIX;
@Value("${aliyun.oss.callback}")
private String CALLBACK;
@Autowired
private OSS ossClient;
@Override
public OssPolicyResult policy() {
OssPolicyResult result = new OssPolicyResult();
// 簽名有效期
long expireEndTime = System.currentTimeMillis() + EXPIRE * 1000;
Date expiration = new Date(expireEndTime);
// 文件名稱
// String filename = UUID.randomUUID().toString();
// 文件大小
long maxSize = MAX_SIZE * 1024 * 1024;
// 提交節(jié)點(diǎn)
String action = "http://" + BUCKET_NAME + "." + ENDPOINT;
// 上傳回調(diào)參數(shù)
Callback callback = new Callback();
// 指定請(qǐng)求回調(diào)的服務(wù)器URL
callback.setCallbackUrl(CALLBACK);
//(可選)設(shè)置回調(diào)請(qǐng)求消息頭中Host的值,即您的服務(wù)器配置Host的值。
// callback.setCallbackHost("yourCallbackHost");
// 設(shè)置發(fā)起回調(diào)時(shí)請(qǐng)求body的值。
callback.setCallbackBody("{\\\"filename\\\":${object}}");
// 設(shè)置發(fā)起回調(diào)請(qǐng)求的Content-Type。
callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
// 設(shè)置發(fā)起回調(diào)請(qǐng)求的自定義參數(shù),由Key和Value組成,Key必須以x:開始。
// callback.addCallbackVar("x:dir", "value");
try {
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, DIR_PREFIX);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
// 將Policy字符串進(jìn)行base64編碼
String policy = BinaryUtil.toBase64String(binaryData);
// 用OSS的AccessKeySecret對(duì)base64編碼后的Policy進(jìn)行簽名
String signature = ossClient.calculatePostSignature(postPolicy);
// 將callback配置進(jìn)行base64編碼
String callbackData = BinaryUtil.toBase64String(OSSUtils.jsonizeCallback(callback).getBytes());
// 返回結(jié)果
result.setAccessKeyId(ACCESS_KEY_ID);
result.setPolicy(policy);
result.setSignature(signature);
// result.setKey(filename);
result.setDir(dir);
result.setHost(action);
result.setCallback(callbackData);
} catch (Exception e) {
log.error("簽名生成失敗", e);
}
return result;
}
@Override
public OssCallBackResult callback(Map<String, Object> requestBody) {
OssCallBackResult ossCallbackResult = new OssCallBackResult();
// 文件名
String filename = requestBody.get("filename").toString();
// 文件鏈接
String url = "https://" + BUCKET_NAME + "." + ENDPOINT + "/" + DIR_PREFIX + "/" + filename;
ossCallbackResult.setUrl(url);
return ossCallbackResult;
}
}
添加 Controller 層:
@Api(tags = "阿里云對(duì)象存儲(chǔ)接口")
@RequestMapping("/api")
@RestController
public class OssController {
@Autowired
private OssService ossService;
@ApiOperation(value = "OSS上傳簽名生成")
@GetMapping("/aliyun/oss/policy")
public Object policy() {
return ossService.policy();
}
@ApiOperation(value = "OSS上傳成功回調(diào)")
@PostMapping("/aliyun/oss/callback")
public Object callback(@RequestBody Map<String, Object> requestBody) {
return ossService.callback(requestBody);
}
}
到此這篇關(guān)于Spring Boot項(xiàng)目中實(shí)現(xiàn)文件上傳功能的示例的文章就介紹到這了,更多相關(guān)Spring Boot實(shí)現(xiàn)文件上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot 單文件上傳的實(shí)現(xiàn)步驟
- Spring Boot 2.x 實(shí)現(xiàn)文件上傳功能
- springboot上傳圖片文件步驟詳解
- springboot+thymeleaf 文件上傳功能的實(shí)現(xiàn)代碼
- springboot+vue實(shí)現(xiàn)文件上傳下載
- 基于springboot實(shí)現(xiàn)文件上傳
- 文件上傳SpringBoot后端MultipartFile參數(shù)報(bào)空問(wèn)題的解決辦法
- springboot操作阿里云OSS實(shí)現(xiàn)文件上傳,下載,刪除功能
- SpringBoot實(shí)現(xiàn)上傳文件到AWS S3的代碼
- 解決springboot項(xiàng)目上傳文件出現(xiàn)臨時(shí)文件目錄為空的問(wèn)題
- SpringBoot實(shí)現(xiàn)本地存儲(chǔ)文件上傳及提供HTTP訪問(wèn)服務(wù)的方法
- Spring Boot應(yīng)用上傳文件時(shí)報(bào)錯(cuò)的原因及解決方案
相關(guān)文章
SpringMvc+Angularjs 實(shí)現(xiàn)多文件批量上傳
本文通過(guò)實(shí)例代碼給大家講解了SpringMvc+Angularjs 實(shí)現(xiàn)多文件批量上傳功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友一起學(xué)習(xí)吧2017-03-03
詳解Java中的有參構(gòu)造方法與無(wú)參構(gòu)造方法
這篇文章主要詳細(xì)介紹了Java中有參構(gòu)造方法與無(wú)參構(gòu)造方法,文中有詳細(xì)的代碼示例,讓大家清晰明了的了解到有參構(gòu)造方法與無(wú)參構(gòu)造方法、以及應(yīng)用,需要的朋友可以參考下2023-06-06
SpringBoot之如何搭建SpringBoot+Maven項(xiàng)目
這篇文章主要介紹了SpringBoot之如何搭建SpringBoot+Maven項(xiàng)目問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
使用Swagger時(shí)Controller中api接口顯示不全的問(wèn)題分析及解決
swagger是一個(gè)十分好用的api接口管理、測(cè)試框架,現(xiàn)在越來(lái)越多的人使用這個(gè)做接口的測(cè)試和管理,但經(jīng)常遇到Controller中的api接口顯示不全的問(wèn)題,所以本文給大家詳細(xì)分析了問(wèn)題以及解決方法,需要的朋友可以參考下2024-02-02

