mall整合OSS實現文件上傳

摘要

本文主要講解mall整合OSS實現文件上傳的過程,採用的是服務端簽名後前端直傳的方式。阿里雲對象存儲服務(Object Storage Service,簡稱OSS),是阿里雲提供的海量、安全、低成本、高可靠的雲存儲服務。OSS可用於圖片、音視頻、日誌等海量文件的存儲。各類終端設備、Web網站程序、移動應用能夠直接向OSS寫入或讀取數據。html

OSS

阿里雲對象存儲服務(Object Storage Service,簡稱 OSS),是阿里雲提供的海量、安全、低成本、高可靠的雲存儲服務。OSS可用於圖片、音視頻、日誌等海量文件的存儲。各類終端設備、Web網站程序、移動應用能夠直接向OSS寫入或讀取數據。前端

OSS中的相關概念

  • Endpoint:訪問域名,經過該域名能夠訪問OSS服務的API,進行文件上傳、下載等操做。
  • Bucket:存儲空間,是存儲對象的容器,全部存儲對象都必須隸屬於某個存儲空間。
  • Object:對象,對象是 OSS 存儲數據的基本單元,也被稱爲 OSS 的文件。
  • AccessKey:訪問密鑰,指的是訪問身份驗證中用到的 AccessKeyId 和 AccessKeySecret。

OSS的相關設置

開通OSS服務

  • 登陸阿里雲官網;
  • 將鼠標移至產品標籤頁,單擊對象存儲 OSS,打開OSS 產品詳情頁面;
  • 在OSS產品詳情頁,單擊當即開通。

建立存儲空間

  • 點擊網頁右上角控制檯按鈕進入控制檯

展現圖片/arch_screen_77.png

  • 選擇個人雲產品中的對象存儲OSS

展現圖片/arch_screen_78.png

  • 點擊左側存儲空間的加號新建存儲空間

展現圖片/arch_screen_79.png

  • 新建存儲空間並設置讀寫權限爲公共讀

展現圖片/arch_screen_80.png

跨域資源共享(CORS)的設置

因爲瀏覽器處於安全考慮,不容許跨域資源訪問,因此咱們要設置OSS的跨域資源共享。java

  • 選擇一個存儲空間,打開其基礎設置

展現圖片/arch_screen_81.png

  • 點擊跨越設置的設置按鈕

展現圖片/arch_screen_82.png

  • 點擊建立規則

展現圖片/arch_screen_83.png

  • 進行跨域規則設置

展現圖片/arch_screen_84.png

服務端簽名後前端直傳的相關說明

流程示例圖

展現圖片/arch_screen_85.png

流程介紹

  1. Web前端請求應用服務器,獲取上傳所需參數(如OSS的accessKeyId、policy、callback等參數)
  2. 應用服務器返回相關參數
  3. Web前端直接向OSS服務發起上傳文件請求
  4. 等上傳完成後OSS服務會回調應用服務器的回調接口
  5. 應用服務器返回響應給OSS服務
  6. OSS服務將應用服務器回調接口的內容返回給Web前端

整合OSS實現文件上傳

在pom.xml中添加相關依賴

<!-- OSS SDK 相關依賴 -->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>2.5.0</version>
</dependency>
複製代碼

修改SpringBoot配置文件

修改application.yml文件,添加OSS相關配置。git

注意:endpoint、accessKeyId、accessKeySecret、bucketName、callback、prefix都要改成你本身賬號OSS相關的,callback須要是公網能夠訪問的地址。github

# 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。web

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;

/** * Created by macro on 2018/5/17. */
@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

前端直接上傳文件時所需參數,從後端返回過來。spring

package com.macro.mall.tiny.dto;

import io.swagger.annotations.ApiModelProperty;

/** * 獲取OSS上傳文件受權返回結果 * Created by macro on 2018/5/17. */
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上傳成功後,會根據該配置參數來回調對應接口。json

package com.macro.mall.tiny.dto;

import io.swagger.annotations.ApiModelProperty;

/** * oss上傳成功後的回調參數 * Created by macro on 2018/5/17. */
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;

/** * oss上傳文件的回調結果 * Created by macro on 2018/5/17. */
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;

/** * oss上傳管理Service * Created by macro on 2018/5/17. */
public interface OssService {
    /** * oss上傳策略生成 */
    OssPolicyResult policy();

    /** * oss上傳成功回調 */
    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;

/** * oss上傳管理Service實現類 * Created by macro on 2018/5/17. */
@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;

/** * Oss相關操做接口 * Created by macro on 2018/4/26. */
@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);
    }

}

複製代碼

進行接口測試

測試獲取上傳策略的接口

展現圖片/arch_screen_66.png

展現圖片/arch_screen_67.png

展現圖片/arch_screen_68.png

啓動mall-admin-web前端項目來測試上傳接口

  • 如何啓動前端項目,具體參考該項目的readme文檔:github.com/macrozheng/…api

  • 點擊添加商品品牌的上傳按鈕進行測試

展現圖片/arch_screen_69.png

  • 會調用兩次請求,第一次訪問本地接口獲取上傳的策略

展現圖片/arch_screen_70.png

展現圖片/arch_screen_71.png

  • 第二次調用oss服務 的接口進行文件上傳

展現圖片/arch_screen_72.png

展現圖片/arch_screen_73.png

  • 能夠看到上面接口調用並無傳入回調參數callback,因此接口返回了204 no content,此次咱們傳入回調參數callback試試,能夠發現oss服務回調了咱們本身定義的回調接口,並返回了相應結果。

展現圖片/arch_screen_74.png

展現圖片/arch_screen_75.png

展現圖片/arch_screen_76.png

項目源碼地址

github.com/macrozheng/…

參考資料

公衆號

mall項目全套學習教程連載中,關注公衆號第一時間獲取。

公衆號圖片
相關文章
相關標籤/搜索