用烂了的《桥接模式》
图片缩略图功能
需要对图片生产缩略图,压缩并保存到不同的介质中。输入端可能是本地图片,也可能是网络图片。保存的位置可能是本地,也有可能是第三方的minio、阿里云oss、七牛云oss。并且这些途径可能随时扩展。为了设计的灵活性,这里就要把输入可输出抽象出来,应用层通过抽象层结偶。这样的抽象和实现分离,实现层可以自由扩展,而应用层依赖于抽象层相对稳定。
整体结构是这样的
代码结构是这样的
整体实现是这样的
ThumbLoad
package com.xxx.modules.news.service.impl.thumb;
import com.xxx.modules.news.entity.ImageActionVo;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import javax.validation.constraints.NotBlank;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 图片载入
* 1)图片载入过程不涉及到异常处理,异常自行处理,一切结果以isSuccess为准,具体消息去message
*/
@Slf4j
public abstract class ThumbLoad {
@Setter
@Getter
protected ThumbSave thumbSave;
public ThumbLoad(ThumbSave thumbSave) {
this.thumbSave = thumbSave;
}
@Getter
@Setter
/*load载入结果,默认为false,免去每次异常都赋值.只有操作成功后进行赋值true*/
protected Boolean isSuccess=false;
@Getter
@Setter
/*错误消息提醒*/
protected String message =null;
public abstract ImageActionVo thumb(String filePath);
/**
* 验证网络资源是否可以访问
* @param url 网络资源地址
* @param checkIsImage 是否强制检测为image类型的资源
* @return
*/
protected Boolean checkUrlIsAccess(@NotBlank String url, Boolean checkIsImage){
log.debug("checkUrlIsAccess start ...");
log.debug("url:{}",url);
URL netUrl = null;
HttpURLConnection httpURLConnection =null;
Boolean result = true;
try {
netUrl = new URL(url);
httpURLConnection = (HttpURLConnection)netUrl.openConnection();
httpURLConnection.setConnectTimeout(2000);
if (httpURLConnection.getResponseCode()==200) {
String contentType = httpURLConnection.getContentType();
if (checkIsImage){
if (!contentType.split("/")[0].equals("image")) {
result = false;
}
}
}else{
result = false;
}
}catch (Exception e){
log.error("e :{}", e);
result = false;
}finally {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
log.debug("checkUrlIsAccess end.");
return result;
}
}
ThumbSave
package com.xxx.modules.news.service.impl.thumb;
import com.xxx.config.FileServerConfig;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.InputStream;
/**
* 文件保存
* 1)文件保存到fileServer ,thumburi是fileserver返回的可以直接访问的地址
* 2)文件保存本地,thumburi表示本地文件的完整路径
* 注:
* 1.文件保存类型取决与具体实现,但结构保持一致。最外层控制逻辑,是isSuccess.
* 2.为了实现最大程度的复用,这里选择使用inputStream 进行对接saveImage.
* 3.在扩展save策略时,注意异常处理,结果都从ImageSaveResult体现说明
*/
public abstract class ThumbSave {
@Autowired
protected FileServerConfig fileServerConfig;
protected String bucketName = "thumb_images"; //file server buckete
protected String saveUri; //处理后的文件保存路
protected Integer width; //图片压缩宽度
protected Integer height; //图片压缩高度
public ThumbSave(String saveUri, Integer width, Integer height) {
this.saveUri = saveUri;
this.width = width;
this.height = height;
}
abstract ImageSaveResult save(InputStream imageInputStream);
@Data
@AllArgsConstructor
class ImageSaveResult {
private Boolean isSuccess = false; //操作是否成功
private String thumbUri; //缩略图文件地址
private String msg; //消息
}
}
下面是具体实现
LocalThumbSave 本地保存
package com.xxx.modules.news.service.impl.thumb;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import java.io.IOException;
import java.io.InputStream;
/**
* 本地保存文件
*/
@Slf4j
public class LocalThumbSave extends ThumbSave {
public LocalThumbSave(String saveUri, Integer width, Integer height) {
super(saveUri, width, height);
}
public ImageSaveResult save(InputStream imageInputStream) {
try {
Thumbnails.of(imageInputStream).size(width,height).toFile(saveUri);
return new ImageSaveResult(true,saveUri,null);
} catch (IOException e) {
log.error("Thumbnails toFiles error:{}",e);
return new ImageSaveResult(false, null, e.getMessage());
}
}
}
FileServerThumbSave file server保存文件(这里的fileserver又做了一层抽象,可以接入不同的第三方)
package com.xxx.modules.news.service.impl.thumb;
import com.xxx.common.SpringContextUtil;
import com.xxx.common.utils.R;
import com.xxx.modules.news.feign.FileFeignClient;
import com.xxx.modules.news.service.impl.G;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* file server保存文件
*/
@Slf4j
public class FileServerThumbSave extends ThumbSave {
public FileServerThumbSave(String saveUri, Integer width, Integer height) {
super(saveUri, width, height);
}
public ImageSaveResult save(final InputStream imageInputStream) {
log.debug("FileServerSaveImage start ......");
try {
@Cleanup ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Thumbnails.of(imageInputStream).size(width,height).toOutputStream(outputStream);
@Cleanup ByteArrayInputStream swapStream = new ByteArrayInputStream(outputStream.toByteArray());
FileFeignClient fileFeignClient = SpringContextUtil.getBean(FileFeignClient.class);
MockMultipartFile mockMultipartFile = new MockMultipartFile("file.jpg","file.jpg", MediaType.IMAGE_JPEG_VALUE,swapStream);
R image = fileFeignClient.upload(bucketName, mockMultipartFile);
log.debug("image :{}", image);
if (G.isSuccess(image)) {
return new ImageSaveResult(true, image.get("data").toString(), null);
}else{
return new ImageSaveResult(false, null, G.getMsg(image));
}
} catch (IOException e) {
log.error("FileServerSaveImage IOException :{}",e);
return new ImageSaveResult(false, null, e.getMessage());
}finally {
log.debug("FileServerSaveImage end");
}
}
}
UriThumbLoad 读取本地资源文件
package com.xxx.modules.news.service.impl.thumb;
import com.xxx.modules.news.entity.ImageActionVo;
import com.xxx.modules.news.enums.ErrorMsgEnum;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* 本地文件载入
*/
@Slf4j
public class UriThumbLoad extends ThumbLoad {
public UriThumbLoad(ThumbSave thumbSave) {
super(thumbSave);
}
public ImageActionVo thumb(String filePath) {
//文件检查
File file = new File(filePath);
if (!file.exists()) {
return new ImageActionVo(filePath,false,null, ErrorMsgEnum.FILE_NOT_FIND.getValue());
}
try {
@Cleanup InputStream inputStream = new FileInputStream(file);
ThumbSave.ImageSaveResult save = thumbSave.save(inputStream);
return new ImageActionVo(filePath, save.getIsSuccess(), save.getThumbUri(), save.getMsg());
} catch (Exception e) {
log.error("FileNotFoundException :{}",e);
return new ImageActionVo(filePath,false,null,e.getMessage());
}
}
}
UrlThumbLoad 读取网络资源文件
package com.xxx.modules.news.service.impl.thumb;
import com.xxx.modules.news.entity.ImageActionVo;
import com.xxx.modules.news.enums.ErrorMsgEnum;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 网络图片载入
*/
@Slf4j
public class UrlThumbLoad extends ThumbLoad {
public UrlThumbLoad(ThumbSave thumbSave) {
super(thumbSave);
}
public ImageActionVo thumb(String filePath) {
HttpURLConnection urlConnection;
if (!checkUrlIsAccess(filePath,true)) {
return new ImageActionVo(filePath, false, null, ErrorMsgEnum.FILE_NOT_ACCESS.getValue());
}
try {
URL url = new URL(filePath);
urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setConnectTimeout(5000);
@Cleanup InputStream inputStream = urlConnection.getInputStream();
ThumbSave.ImageSaveResult save = thumbSave.save(inputStream);
return new ImageActionVo(filePath, save.getIsSuccess(), save.getThumbUri(),save.getMsg());
} catch (Exception e) {
log.error("FileNotFoundException :{}",e);
return new ImageActionVo(filePath,false,null,e.getMessage());
}
}
}
文件压缩也是同样的实现
实际项目中无形中就用到了这些模式,也不用刻意去硬套模式。
在即将结束的职业生涯余光里,没事记录一下自己的代码风格。
本文来自博客园,作者:Eular,转载请注明原文链接:https://www.cnblogs.com/euler-blog/p/18611246