一、前言
最近在看一个开源项目,发现集成了本地存储以及云存储。大致看了一下思路,感觉也对面向对象的思想有了更进一步的理解。特来记录一下。
下文的描述不是很严谨,带有自己的理解存在。
如果有错误或者表述不恰当,欢迎指正。
二、思路
三、关键性的代码
以实现图片本地存储为例子
yml文件
storage:
# 当前工作的对象存储模式,分别是local、aliyun、tencent、qiniu
active: local
# 本地对象存储配置信息
local:
# 存储路径
storagePath: storage
# 访问地址
address: http://localhost:1203/storage/fetch/
读取yaml值的类
@ConfigurationProperties其实就类似于使用多个@Value同时绑定,绑定的对象就是隐式绑定yaml里面的storage里面的值,意味着在配置文件编写的时候需要与对应yaml的字段名称相同
@ConfigurationProperties(prefix = "storage")
@Data
public class StorageProperties {
private String active;
private Local local;
public static class Local {
private String address;
private String storagePath;
// 省略get与set方法
}
}
配置类获取绑定配置参数
@Configuration
@EnableConfigurationProperties(StorageProperties.class)
public class StorageAutoConfiguration {
private final StorageProperties properties;
public StorageAutoConfiguration(StorageProperties properties) {
this.properties = properties;
}
/**
* 获取配置参数里面的active值,通过active的不同,基础的存储服务类的中的存储模式也不同
*
* @author hanyulin
* @date 2021/12/4 10:57
* @return BaseStorageService
*/
@Bean
public BaseStorageService baseStorageService() {
BaseStorageService baseStorageService = new BaseStorageService();
String active = this.properties.getActive();
baseStorageService.setActive(active);
if ("local".equals(active)) {
baseStorageService.setStorageType(localStorage());
}
else {
throw new RuntimeException("当前存储模式 " + active + " 不支持");
}
return baseStorageService;
}
/**
* 存储模式为local,注入LocalStorageImpl需要的属性
*
* @author hanyulin
* @date 2021/12/4 11:11
* @return com.hanliy.storage.impl.LocalStorageImpl
*/
@Bean
public LocalStorageImpl localStorage() {
LocalStorageImpl localStorage = new LocalStorageImpl();
StorageProperties.Local local = this.properties.getLocal();
localStorage.setAddress(local.getAddress());
localStorage.setStoragePath(local.getStoragePath());
return localStorage;
}
}
controller类
controller不做业务逻辑的处理
@PostMapping("/upload")
public Result<PicStorageEntity> create(@RequestParam("file") MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
PicStorageEntity picStorage = baseStorageService.store(file.getInputStream(), file.getSize(),
file.getContentType(), originalFilename);
return new Result<PicStorageEntity>().ok(picStorage);
}
基本的文件存储的业务类
负责对图片存储以及图片信息存储的转发
/**
* @author hanyulin
* @apiNote 基本的图片存储服务类,负责对图片存储以及图片信息存储的转发
* @date 2021/12/3 16:27
*/
@Data
public class BaseStorageService {
private String active;
private StorageType storageType;
@Resource
private StorageService storageService;
/**
* 图片存储
*
* @param inputStream 图片流
* @param contentLength 图片大小
* @param contentType 图片的类型
* @param fileName 图片原名称
* @author hanyulin
* @date 2021/12/4 11:24
* @return PicStorageEntity
*/
public PicStorageEntity store(InputStream inputStream, long contentLength, String contentType, String fileName) {
/*
* 产生图片唯一标识符号
*/
String key = generateKey(fileName);
/*
* 调用接口StorageType的store实现文件存储
*/
storageType.store(inputStream, contentLength, contentType, key);
/*
* 图片信息的存储
*/
PicStorageEntity picStorage = new PicStorageEntity();
picStorage.setName(fileName);
picStorage.setSize((int) contentLength);
picStorage.setType(contentType);
picStorage.setPicKey(key);
picStorage.setUrl(storageType.generateUrl(key));
storageService.insert(picStorage);
return picStorage;
}
}
存储服务接口类
提供不同的存储服务类,所有存储服务均由该类对外提供
/**
* @author hanyulin
* @apiNote 提供不同的存储服务类,所有存储服务均由该类对外提供
* @date 2021/12/3 9:49
*/
public interface StorageType {
/**
* 存储一个文件对象
*
* @param inputStream 文件输入流
* @param contentLength 文件长度
* @param contentType 文件类型
* @param keyName 文件名
*/
void store(InputStream inputStream, long contentLength, String contentType, String keyName);
/**
* 通过唯一标识,产生访问路径
*
* @param keyName 唯一标识名称
* @return 访问路径
*/
String generateUrl(String keyName);
}
本地存储实现类
@Data
public class LocalStorageImpl implements StorageType {
private String storagePath;
private String address;
private Path rootLocation;
/*
* 创建图片的存储目录
*/
public void setStoragePath(String storagePath) {
this.storagePath = storagePath;
this.rootLocation = Paths.get(storagePath);
try {
Files.createDirectories(rootLocation);
} catch (IOException e) {
e.getMessage();
}
}
@Override
public void store(InputStream inputStream, long contentLength, String contentType, String keyName) {
try {
Files.copy(inputStream, rootLocation.resolve(keyName), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException("Failed to store file " + keyName, e);
}
}
@Override
public String generateUrl(String keyName) {
return address + keyName;
}
四、测试
Q:在某个公众号看到一道面试题:一道面试题,GET 请求能传图片吗?
图片一般有两种传输方式:base64 和 file对象.
base64 :在客户端,由于浏览器地址栏的限制,只能够上传小图片(Blob、TypedArray、图片压缩方案)。在服务端,可以传任意大小的图片。
file:可以传输的,数据在body里面(GET 请求有 body 体。GET 和 POST 并没有本质上的区别,他们只是 HTTP 协议中两种请求方式,仅仅是报文格式不同(或者说规范不同)。)。
GET 请求能不能带 body 这个事是由 HTTP 协议来定义的。是一种规范。