代理模式
**案例:**根据文件类型,将文件存储到不同服务
代理模式:给一个对象创建一个代理对象,通过代理对象可以使用该对象的功能。
CGLib和JDK 是代理模式实现的技术方案。
**场景:**在大的项目中,文件上传功能,因为大项目文件比较多,所以不可能存储到部署代码的服务 器中,所以可能要把文件上传到其他服务器中(或者云服务器),现在我们就把.mp4,.avi 类型文件放到阿里云服务器,把jpg,png 放到FastDFS 服务器(分布式文件系统)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BlI4EwbL-1606802064839)(C:\Users\EDZ\AppData\Roaming\Typora\typora-user-images\image-20201201102435013.png)]
实现分析: 基于代理模式,我们实现文件上传分别路由到aliyunOSS 和FastDFS,用例图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lqdrrxDH-1606802064841)(C:\Users\EDZ\AppData\Roaming\Typora\typora-user-images\image-20201201102816695.png)]
前端首先要访问FileController,FileController 注入FileUploadProxy 代理对象,然后调用FileUploadProxy 代理对象的方法。然后写阿里云服务器的文件上传接口,FastDFS 服务器的文件上传接口,或者华为云的文件上传功能,我们要遵循开闭原则,**开放扩展,关闭修改,**所以尽量(一定不要,因为是有技术实现不修改代码的,可能是自己技术没那么强,暂时想不出办法来)不要修改代码,现在就是写一个接口,定义一个upload 方法,所有的文件上传功能都实现这个接口,然后通过FileController 得到文件的后缀名称,通过文件的后缀名判断调用哪个对象的方法。
FileController:是控制器,用于接收用户提交的文件,并调用代理FileUploadProxy实现文件上传。
@RestController
@RequestMapping(value = "/file")
public class FileController {
@Autowired
private FileUploadProxy fileUploadProxy;
/***
* 文件上传
* @param file
* @return
* @throws IOException
*/
@PostMapping(value = "/upload")
public String upload(MultipartFile file) throws Exception {
return fileUploadProxy.upload(file);
}
}
FileUploadProxy:是代理对象,供用户访问,调用了FileUpload的文件上传方法,为用户提供不同文件上传调用。
@ConfigurationProperties(prefix = "upload")
@Component(value = "fileUploadProxy")
public class FileUploadProxy {
@Autowired
private ApplicationContext act;
private Map<String,List<String>> filemap;
/****
* 文件上传
* @param file
* @return
*/
public String upload(MultipartFile file) throws Exception{
//1.文件的字节数组
byte[] buffers = file.getBytes();
//2.文件的扩展名 1.jpg
String fileName = file.getOriginalFilename();
String extName = StringUtils.getFilenameExtension(fileName); //jpg|mp4|png
/***
* 3.1 aliyunOSSFileUpload----->mp4,avi
* 3.2 fastdfsFileUpoad----->png,jpg,gif
*/
for (Map.Entry<String, List<String>> entry : filemap.entrySet()) {
//获取后缀集合
for (String suffix : entry.getValue()) {
//循环迭代匹配后缀
if(suffix.equalsIgnoreCase(extName)){
return act.getBean(entry.getKey(),FileUpload.class).upload(buffers,extName);
}
}
}
return null;
}
/****
* 文件上传
* @param file
* @return
*/
public String upload_backup(MultipartFile file) throws Exception{
//1.文件的字节数组
byte[] buffers = file.getBytes();
//2.文件的扩展名 1.jpg
String fileName = file.getOriginalFilename();
String extName = StringUtils.getFilenameExtension(fileName); //jpg|mp4|png
/***
* 3.1 aliyunOSSFileUpload----->mp4,avi
* 3.2 fastdfsFileUpoad----->png,jpg,gif
*/
//1.判断文件后缀
if(extName.equalsIgnoreCase("mp4") || extName.equalsIgnoreCase("avi")){
//2.根据文件后缀获取指定对象的实例----->ApplicationContext获取实例
return act.getBean("aliyunOSSFileUpload",FileUpload.class).upload(buffers,extName);
}else if(extName.equalsIgnoreCase("png") || extName.equalsIgnoreCase("jpg")){
return act.getBean("fastdfsFileUpoad",FileUpload.class).upload(buffers,extName);
}
return null;
}
//依赖注入
public void setFilemap(Map<String, List<String>> filemap) {
this.filemap = filemap;
}
}
FileUpload: 抽象接口,定义了文件上传方法,分别给它写了2种实现。
public interface FileUpload {
/****
* 文件上传方法
* 返回值:上传的文件访问路径
*/
String upload(byte[] buffers,String extName);
}
FastdfsFileUpload:是将文件上传到FastDFS,主要上传png和jpg的图片小文件。
@Component(value = "fastdfsFileUpoad")
public class FastdfsFileUpoad implements FileUpload{
@Value("${fastdfs.url}")
private String url;
/***
* 文件上传
* @param buffers:文件字节数组
* @param extName:后缀名
* @return
*/
@Override
public String upload(byte[] buffers, String extName) {
/***
* 文件上传后的返回值
* uploadResults[0]:文件上传所存储的组名,例如:group1
* uploadResults[1]:文件存储路径,例如:M00/00/00/wKjThF0DBzaAP23MAAXz2mMp9oM26.jpeg
*/
String[] uploadResults = null;
try {
//获取StorageClient对象
StorageClient storageClient = getStorageClient();
//执行文件上传
uploadResults = storageClient.upload_file(buffers, extName, null);
return url+uploadResults[0]+"/"+uploadResults[1];
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/***
* 初始化tracker信息
*/
static {
try {
//获取tracker的配置文件fdfs_client.conf的位置
String filePath = new ClassPathResource("fdfs_client.conf").getPath();
//加载tracker配置信息
ClientGlobal.init(filePath);
} catch (Exception e) {
e.printStackTrace();
}
}
/***
* 获取StorageClient
* @return
* @throws Exception
*/
public static StorageClient getStorageClient() throws Exception{
//创建TrackerClient对象
TrackerClient trackerClient = new TrackerClient();
//通过TrackerClient获取TrackerServer对象
TrackerServer trackerServer = trackerClient.getConnection();
//通过TrackerServer创建StorageClient
StorageClient storageClient = new StorageClient(trackerServer,null);
return storageClient;
}
}
AliyunOSSFileUpload:是将文件上传到aliyunOSS,主要上传mp4和avi 的视频大文件
@Component(value = "aliyunOSSFileUpload")
public class AliyunOSSFileUpload implements FileUpload{
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKey}")
private String accessKey;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.key}")
private String key;
@Value("${aliyun.oss.bucketName}")
private String bucketName;
@Value("${aliyun.oss.backurl}")
private String backurl;
/****
* 文件上传
* 文件类型如果是图片,则上传到本地FastDFS
* 文件类型如果是视频,则上传到aliyun OSS
*/
@Override
public String upload(byte[] buffers,String extName) {
String realName = UUID.randomUUID().toString()+"."+extName ;
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKey, accessKeySecret);
// <yourObjectName>表示上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key+realName, new ByteArrayInputStream(buffers));
// 上传字符串。
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(FileUtil.getContentType("."+extName));
putObjectRequest.setMetadata(objectMetadata);
ossClient.putObject(putObjectRequest);
// 关闭OSSClient。
ossClient.shutdown();
return backurl+realName;
}
}
application.yml 配置:这里配置的是aliyun ,FastDFS的连接信息
upload 这块配置的是一个map,key,value 形式的,我们在FileController 中获取这段配置,然后获取用户上传文件的后缀名,和这个配置比较,上传到指定服务器中。
server:
port: 18081
#指定服务处理指定的文件类型
upload:
filemap:
aliyunOSSFileUpload: mp4,avi
fastdfsFileUpload: png,jpg
#FastDFS配置
fastdfs:
url: http://192.168.211.137:28181/
#aliyun
aliyun:
oss:
FileUpload接口定义:
AliyunOSSFileUpload实现:
endpoint: oss-cn-beijing.aliyuncs.com
accessKey: a7i6rVEjbtaJdYX2
accessKeySecret: MeSZPybPHfJtsYCRlEaUbfRtdH8gl4
bucketName: sklll
key: video/
backurl: https://sklll.oss-cn-beijing.aliyuncs.com/video/ #访问地址配置
spring:
servlet:
multipart:
max-file-size: 100MB #上传文件大小配置
g.aliyuncs.com
accessKey: a7i6rVEjbtaJdYX2
accessKeySecret: MeSZPybPHfJtsYCRlEaUbfRtdH8gl4
bucketName: sklll
key: video/
backurl: https://sklll.oss-cn-beijing.aliyuncs.com/video/ #访问地址配置
spring:
servlet:
multipart:
max-file-size: 100MB #上传文件大小配置