本文主要講解mall整合OSS實作檔案上傳的過程,采用的是服務端簽名後前端直傳的方式。
OSS
阿裡雲對象存儲服務(Object Storage Service,簡稱 OSS),是阿裡雲提供的海量、安全、低成本、高可靠的雲存儲服務。OSS可用于圖檔、音視訊、日志等海量檔案的存儲。各種終端裝置、Web網站程式、移動應用可以直接向OSS寫入或讀取資料。
OSS中的相關概念
- Endpoint:通路域名,通過該域名可以通路OSS服務的API,進行檔案上傳、下載下傳等操作。
- Bucket:存儲空間,是存儲對象的容器,所有存儲對象都必須隸屬于某個存儲空間。
- Object:對象,對象是 OSS 存儲資料的基本單元,也被稱為 OSS 的檔案。
- AccessKey:通路密鑰,指的是通路身份驗證中用到的 AccessKeyId 和 AccessKeySecret。
OSS的相關設定
開通OSS服務
- 登入阿裡雲官網;
- 将滑鼠移至産品标簽頁,單擊對象存儲 OSS,打開OSS 産品詳情頁面;
- 在OSS産品詳情頁,單擊立即開通。
建立存儲空間
- 點選網頁右上角控制台按鈕進入控制台

- 選擇我的雲産品中的對象存儲OSS
- 點選左側存儲空間的加号建立存儲空間
- 建立存儲空間并設定讀寫權限為公共讀
跨域資源共享(CORS)的設定
由于浏覽器處于安全考慮,不允許跨域資源通路,是以我們要設定OSS的跨域資源共享。
- 選擇一個存儲空間,打開其基礎設定
- 點選跨越設定的設定按鈕
- 點選建立規則
- 進行跨域規則設定
服務端簽名後前端直傳的相關說明
流程示例圖
流程介紹
- Web前端請求應用伺服器,擷取上傳所需參數(如OSS的accessKeyId、policy、callback等參數)
- 應用伺服器傳回相關參數
- Web前端直接向OSS服務發起上傳檔案請求
- 等上傳完成後OSS服務會回調應用伺服器的回調接口
- 應用伺服器傳回響應給OSS服務
- OSS服務将應用伺服器回調接口的内容傳回給Web前端
整合OSS實作檔案上傳
在pom.xml中添加相關依賴
com.aliyun.oss
aliyun-sdk-oss
2.5.0
修改SpringBoot配置檔案
修改application.yml檔案,添加OSS相關配置。
注意:endpoint、accessKeyId、accessKeySecret、bucketName、callback、prefix都要改為你自己帳号OSS相關的,callback需要是公網可以通路的位址。
# OSS相關配置資訊
aliyun:
oss:
endpoint: oss-cn-shenzhen.aliyuncs.com # oss對外服務的通路域名
accessKeyId: test # 通路身份驗證中用到使用者辨別
accessKeySecret: test # 使用者用于加密簽名字元串和oss用來驗證簽名字元串的密鑰
bucketName: macro-oss # oss的存儲空間
policy:
expire: 300 # 簽名有效期(S)
maxSize: 10 # 上傳檔案大小(M)
callback: http://localhost:8080/aliyun/oss/callback # 檔案上傳成功後的回調位址
dir:
prefix: mall/images/ # 上傳檔案夾路徑字首
添加OSS的相關Java配置
用于配置OSS的連接配接用戶端OSSClient。
package com.macro.mall.tiny.config;
import com.aliyun.oss.OSSClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OssConfig {
@Value("${aliyun.oss.endpoint}")
private String ALIYUN_OSS_ENDPOINT;
@Value("${aliyun.oss.accessKeyId}")
private String ALIYUN_OSS_ACCESSKEYID;
@Value("${aliyun.oss.accessKeySecret}")
private String ALIYUN_OSS_ACCESSKEYSECRET;
@Bean
public OSSClient ossClient(){
return new OSSClient(ALIYUN_OSS_ENDPOINT,ALIYUN_OSS_ACCESSKEYID,ALIYUN_OSS_ACCESSKEYSECRET);
}
}
添加OSS上傳政策封裝對象OssPolicyResult
前端直接上傳檔案時所需參數,從後端傳回過來。
package com.macro.mall.tiny.dto;
import io.swagger.annotations.ApiModelProperty;
public class OssPolicyResult {
@ApiModelProperty("通路身份驗證中用到使用者辨別")
private String accessKeyId;
@ApiModelProperty("使用者表單上傳的政策,經過base64編碼過的字元串")
private String policy;
@ApiModelProperty("對policy簽名後的字元串")
private String signature;
@ApiModelProperty("上傳檔案夾路徑字首")
private String dir;
@ApiModelProperty("oss對外服務的通路域名")
private String host;
@ApiModelProperty("上傳成功後的回調設定")
private String callback;
//省略了所有getter,setter方法
}
添加OSS上傳成功後的回調參數對象OssCallbackParam
當OSS上傳成功後,會根據該配置參數來回調對應接口。
package com.macro.mall.tiny.dto;
import io.swagger.annotations.ApiModelProperty;
public class OssCallbackParam {
@ApiModelProperty("請求的回調位址")
private String callbackUrl;
@ApiModelProperty("回調是傳入request中的參數")
private String callbackBody;
@ApiModelProperty("回調時傳入參數的格式,比如表單送出形式")
private String callbackBodyType;
//省略了所有getter,setter方法
}
OSS上傳成功後的回調結果對象OssCallbackResult
回調接口中傳回的資料對象,封裝了上傳檔案的資訊。
package com.macro.mall.tiny.dto;
import io.swagger.annotations.ApiModelProperty;
public class OssCallbackResult {
@ApiModelProperty("檔案名稱")
private String filename;
@ApiModelProperty("檔案大小")
private String size;
@ApiModelProperty("檔案的mimeType")
private String mimeType;
@ApiModelProperty("圖檔檔案的寬")
private String width;
@ApiModelProperty("圖檔檔案的高")
private String height;
//省略了所有getter,setter方法
}
添加OSS業務接口OssService
package com.macro.mall.tiny.service;
import com.macro.mall.tiny.dto.OssCallbackResult;
import com.macro.mall.tiny.dto.OssPolicyResult;
import javax.servlet.http.HttpServletRequest;
public interface OssService {
OssPolicyResult policy();
OssCallbackResult callback(HttpServletRequest request);
}
添加OSS業務接口OssService的實作類OssServiceImpl
package com.macro.mall.tiny.service.impl;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.macro.mall.tiny.dto.OssCallbackParam;
import com.macro.mall.tiny.dto.OssCallbackResult;
import com.macro.mall.tiny.dto.OssPolicyResult;
import com.macro.mall.tiny.service.OssService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
@Service
public class OssServiceImpl implements OssService {
private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class);
@Value("${aliyun.oss.policy.expire}")
private int ALIYUN_OSS_EXPIRE;
@Value("${aliyun.oss.maxSize}")
private int ALIYUN_OSS_MAX_SIZE;
@Value("${aliyun.oss.callback}")
private String ALIYUN_OSS_CALLBACK;
@Value("${aliyun.oss.bucketName}")
private String ALIYUN_OSS_BUCKET_NAME;
@Value("${aliyun.oss.endpoint}")
private String ALIYUN_OSS_ENDPOINT;
@Value("${aliyun.oss.dir.prefix}")
private String ALIYUN_OSS_DIR_PREFIX;
@Autowired
private OSSClient ossClient;
@Override
public OssPolicyResult policy() {
OssPolicyResult result = new OssPolicyResult();
// 存儲目錄
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date());
// 簽名有效期
long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
Date expiration = new Date(expireEndTime);
// 檔案大小
long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
// 回調
OssCallbackParam callback = new OssCallbackParam();
callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
callback.setCallbackBodyType("application/x-www-form-urlencoded");
// 送出節點
String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
try {
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String policy = BinaryUtil.toBase64String(binaryData);
String signature = ossClient.calculatePostSignature(postPolicy);
String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8"));
// 傳回結果
result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
result.setPolicy(policy);
result.setSignature(signature);
result.setDir(dir);
result.setCallback(callbackData);
result.setHost(action);
} catch (Exception e) {
LOGGER.error("簽名生成失敗", e);
}
return result;
}
@Override
public OssCallbackResult callback(HttpServletRequest request) {
OssCallbackResult result= new OssCallbackResult();
String filename = request.getParameter("filename");
filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);
result.setFilename(filename);
result.setSize(request.getParameter("size"));
result.setMimeType(request.getParameter("mimeType"));
result.setWidth(request.getParameter("width"));
result.setHeight(request.getParameter("height"));
return result;
}
}
添加OssController定義接口
package com.macro.mall.tiny.controller;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.dto.OssCallbackResult;
import com.macro.mall.tiny.dto.OssPolicyResult;
import com.macro.mall.tiny.service.impl.OssServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@Api(tags = "OssController", description = "Oss管理")
@RequestMapping("/aliyun/oss")
public class OssController {
@Autowired
private OssServiceImpl ossService;
@ApiOperation(value = "oss上傳簽名生成")
@RequestMapping(value = "/policy", method = RequestMethod.GET)
@ResponseBody
public CommonResult<OssPolicyResult> policy() {
OssPolicyResult result = ossService.policy();
return CommonResult.success(result);
}
@ApiOperation(value = "oss上傳成功回調")
@RequestMapping(value = "callback", method = RequestMethod.POST)
@ResponseBody
public CommonResult<OssCallbackResult> callback(HttpServletRequest request) {
OssCallbackResult ossCallbackResult = ossService.callback(request);
return CommonResult.success(ossCallbackResult);
}
}
進行接口測試
測試擷取上傳政策的接口
啟動mall-admin-web前端項目來測試上傳接口
- 如何啟動前端項目,具體參考該項目的readme文檔:https://github.com/macrozheng/mall-admin-web
- 點選添加商品品牌的上傳按鈕進行測試
- 會調用兩次請求,第一次通路本地接口擷取上傳的政策
- 第二次調用oss服務 的接口進行檔案上傳
- 可以看到上面接口調用并沒有傳入回調參數callback,是以接口傳回了204 no content,這次我們傳入回調參數callback試試,可以發現oss服務回調了我們自己定義的回調接口,并傳回了相應結果。
項目源碼位址
https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-09
參考資料
- 開通OSS服務:https://help.aliyun.com/document_detail/31884.html?spm=a2c4g.11186623.6.566.74b87eaebrfQno
- 建立存儲空間:https://help.aliyun.com/document_detail/31885.html?spm=a2c4g.11186623.6.567.496228bcVZUZqB
- 跨域資源共享(CORS):https://help.aliyun.com/document_detail/31928.html?spm=5176.11065259.1996646101.searchclickresult.4d1a5607Pf3e9i
- 服務端簽名直傳并設定上傳回調:https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.11186623.6.1268.2c256506mNqV1t
推薦閱讀
- mall架構及功能概覽
- mall學習所需知識點(推薦資料)
- mall整合SpringBoot+MyBatis搭建基本骨架
- mall整合Swagger-UI實作線上API文檔
- mall整合Redis實作緩存功能
- mall整合SpringSecurity和JWT實作認證和授權(一)
- mall整合SpringSecurity和JWT實作認證和授權(二)
- mall整合SpringTask實作定時任務
- mall整合Elasticsearch實作商品搜尋
- mall整合Mongodb實作文檔操作
- mall整合RabbitMQ實作延遲消息
歡迎關注,點個在看