一, 簡介
項目中我們可能會遇到上傳文件到雲上,有阿里, 百度雲BOS(Baidu Object Storage), 騰訊COS(Cloud Object Storage), 七牛 等等, 統一名稱都叫 對象存儲服務. 這裏不主要講文件上傳,因爲官方給的文檔已經非常非常的詳細啦, 我主要寫一下在這個文件上傳的過程中工廠模式的應用;
COS文檔: https://cloud.tencent.com/document/product/436/12263#object-api-.E6.8F.8F.E8.BF
工廠模式, 什麼是工廠模式? 我想在spring使用了這麼久,仍然不知道的話,那就和我一樣啦.
維基百科的解釋:
工廠方法模式(英語:Factory method pattern)是一種實現了“工廠”概念的面向對象設計模式。就像其他創建型模式一樣,它也是處理在不指定對象具體類型的情況下創建對象的問題。工廠方法模式的實質是“定義一個創建對象的接口,但讓實現這個接口的類來決定實例化哪個類。工廠方法讓類的實例化推遲到子類中進行。”
創建一個對象常常需要複雜的過程,所以不適合包含在一個複合對象中。創建對象可能會導致大量的重複代碼,可能會需要複合對象訪問不到的信息,也可能提供不了足夠級別的抽象,還可能並不是複合對象概念的一部分。工廠方法模式通過定義一個單獨的創建對象的方法來解決這些問題。由子類實現這個方法來創建具體類型的對象。
整理一下,具體的意思就是 爲了方便拓展和降低耦合,將複雜的多樣的並且有有相同功能的對象進行抽象,由不同的子類來實現不同的功能, 由工廠負責進行子類的實例化.
二, 在代碼中的應用
1, 我們進行騰訊雲的對象操作,當然少不了他們的SDK:
<dependency> <!-- 騰訊雲 -->
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.4.2</version>
</dependency>
2, application.properties 中的自定義配置:
#騰訊雲
oss.cloud.type=1
oss.cloud.qcloudDomain=https://xxxxx-xxxxxxxx.cos.ap-chengdu.myqcloud.com
oss.cloud.qcloudRegion=ap-chengdu
oss.cloud.qcloudSecretId=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
oss.cloud.qcloudSecretKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
oss.cloud.qcloudBucketName=xxxxx-xxxxxxxxxxx
3, bean 配置: CloudConfig.java
package com.gy.fast.common.config.oss;
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.URL;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import com.gy.fast.common.validator.AliyunGroup;
import com.gy.fast.common.validator.QcloudGroup;
import com.gy.fast.common.validator.QiniuGroup;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 雲存儲配置信息
* @author geYang
* @date 2018-05-21
*/
@Component
@ConfigurationProperties(prefix = "oss.cloud")
public class CloudConfig {
/**
* 類型 0:本地, 1:騰訊, 2:阿里雲 , 3:七牛
*/
private Integer type;
/**
* 騰訊雲綁定的域名
*/
private String qcloudDomain;
/**
* 騰訊雲路徑前綴
*/
private String qcloudPrefix;
/**
* 騰訊雲AppId
*/
private String qcloudAppId;
/**
* 騰訊雲SecretId
*/
private String qcloudSecretId;
/**
* 騰訊雲SecretKey
*/
private String qcloudSecretKey;
/**
* 騰訊雲BucketName
*/
private String qcloudBucketName;
/**
* 騰訊雲COS所屬地區
*/
private String qcloudRegion;
// get / set
// toString()
}
4, 到工廠模式啦, 首先我們要將上傳的方法抽象出來
package com.gy.fast.common.config.oss;
import java.io.IOException;
import java.util.Date;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import com.gy.fast.common.util.DateUtil;
/**
* 文件上傳基礎類
* @author geYang
* @date 2018-05-21
*/
public abstract class BaseUpload {
/**
* 生成上傳文件名(key:COS的文件路徑,即從Bucket根路徑開始)
* @param file 文件
* @param path 目錄
* @return 上傳文件名
* @author geYang
* @date 2018-05-21 17:51
*/
protected final String getKey(MultipartFile file, String path) {
// 獲取文件原先名稱
String originalName = file.getOriginalFilename();
// 取到上傳圖片的後綴名
String suffix= originalName.substring(originalName.lastIndexOf("."));
// 生成新的文件名
StringBuffer key = new StringBuffer();
if (!StringUtils.isBlank(path)) {
key.append(path).append("/");
}
key.append(DateUtil.format(new Date(),"yyyyMMddHHmmss"))
.append(RandomStringUtils.randomAlphabetic(5))
.append(suffix);
return key.toString();
}
/**
* 獲取訪問路徑
* @param domain 訪問域名
* @param key 文件名稱
* @return 訪問路徑
* @author geYang
* @date 2018-05-22 17:57
*/
protected final String getUrl (String domain, String key) {
return domain + "/" + key;
}
/**
* 文件上傳
* @param file
* @param path
* @return 文件訪問鏈接
* @throws IOException
* @author geYang
* @date 2018-05-22 10:09
*/
public abstract String upload(MultipartFile file, String path) throws IOException;
/**
* 文件刪除
* @param key 文件訪問路徑
* @author geYang
* @date 2018-05-22 10:13
*/
public abstract void delete(String url);
}
5, 編寫COS上傳的實現:
package com.gy.fast.common.config.oss;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.region.Region;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 騰訊雲存儲
* @author geYang
* @date 2018-05-21
*/
public class QcloudUpload extends BaseUpload {
private CloudConfig cloudConfig;
// 加載配置文件
public QcloudUpload(CloudConfig cloudConfig) {
this.cloudConfig = cloudConfig;
}
/**
* 生成COS客戶端
* @return
* @author geYang
* @date 2018-05-21 16:34
*/
private COSClient getCosClient() {
System.err.println(cloudConfig);
System.err.println(cloudConfig.getQcloudSecretId());
System.err.println(cloudConfig.getQcloudSecretKey());
// 1 初始化用戶身份信息(secretId, secretKey)
COSCredentials cred = new BasicCOSCredentials(cloudConfig.getQcloudSecretId(), cloudConfig.getQcloudSecretKey());
// 2設置bucket的區域, COS地域的簡稱請參照 https://cloud.tencent.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region(cloudConfig.getQcloudRegion()));
// 3生成COS客戶端
COSClient cosclient = new COSClient(cred, clientConfig);
return cosclient;
}
/**
* 文件上傳
* @param path
* @param file
* @return 訪問路徑
* @throws IOException
* @author geYang
* @date 2018-05-21 16:33
*/
@Override
public String upload(MultipartFile file, String path) throws IOException {
FileInputStream fileInputStream = (FileInputStream) file.getInputStream();
ObjectMetadata objectMetadata = new ObjectMetadata();
// 設置輸入流長度(需提前告知輸入流的長度,否則可能導致OOM)
objectMetadata.setContentLength(file.getSize());
String key = getKey(file, path);
COSClient cosClient = getCosClient();
PutObjectResult putObjectResult = cosClient.putObject(cloudConfig.getQcloudBucketName(), key, fileInputStream, objectMetadata);
String etag = putObjectResult.getETag();
cosClient.shutdown();
if (StringUtils.isBlank(etag)) {
return null;
}
// 訪問路徑
return getUrl(cloudConfig.getQcloudDomain(), key);
}
/**
* 刪除文件
* @param key
* @author geYang
* @date 2018-05-21 16:33
*/
@Override
public void delete(String url) {
String bucketName = cloudConfig.getQcloudBucketName();
// 截取域名之後的文件名
String key = url.substring(bucketName.length()+1);
COSClient cosClient = getCosClient();
// 進行刪除
cosClient.deleteObject(cloudConfig.getQcloudBucketName(), key);
cosClient.shutdown();
}
}
6, 編寫工廠進行實實例化上傳類, 通過調用該工廠,來完成上傳任務.
package com.gy.fast.common.config.oss;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 上傳文件工廠
* @author geYang
* @date 2018-05-22
*/
@Component
public final class UploadFactory {
/**
* 雲配置信息
*/
@Autowired
private CloudConfig cloudConfig;
/**
* 實例化上傳類
* @return
* @author geYang
* @date 2018-05-22 18:20
*/
public BaseUpload build() {
int type = cloudConfig.getType();
if (type == 1) {
System.out.println("騰訊雲存儲");
return new QcloudUpload(cloudConfig);
}
if (type == 2) {
System.out.println("阿里雲存儲");
return new BaiduyunUpload(cloudConfig);
}
if (type == 3) {
System.out.println("七牛雲存儲");
return new QiiuUpload(cloudConfig);
} else {
System.out.println("本地存儲");
return new MyUpload();
}
}
}
7, 調用應用:
package com.gy.fast.module.sys.controller;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.gy.fast.common.config.oss.UploadFactory;
import com.gy.fast.common.util.R;
/**
* 文件上傳
* @author geYang
* @date 2018-05-21
*/
@RestController
public class SysOssController {
@Autowired
protected UploadFactory uploadFactory;
/**
* 上傳文件
*/
@PostMapping("/upload")
public R upload(@RequestParam("file") MultipartFile file) throws Exception {
System.out.println(getUser());
if (file.isEmpty()) {
throw new SysException("上傳文件不能爲空");
}
//上傳文件
String url = uploadFactory.build().upload(file, "");
System.out.println(url);
return R.ok().put("url", url);
}
}
到此, 關於工廠模式的演示基本完成了, 集成其他雲存儲配置的話只需要實現BaseUpload就好.