一, 简介
项目中我们可能会遇到上传文件到云上,有阿里, 百度云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就好.