文章插图
结构与架构图一致
事件实现事件串联了整个上传流程:
- 文件上传,触发UploadEvent
- UploadListener监听到UploadEvent,委托各个Converter进行文件处理
- 转换完成后触发ConvertEvent
- ConvertListener监听到ConvertEvent后,进行转换后的信息处理
// 配置线程池,Spring默认线程池没有设置大小,如果出现阻塞,可能会出现OOM@Bean("eventThread") public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 设置核心线程数,转换是个很耗时的过程,所以直接排队执行 executor.setCorePoolSize(1); // 设置最大线程数 executor.setMaxPoolSize(1); // 设置队列容量 executor.setQueueCapacity(100); // 设置线程活跃时间(秒) executor.setKeepAliveSeconds(60); // 设置默认线程名称 executor.setThreadNamePrefix("eventThread-"); // 设置拒绝策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任务结束后再关闭线程池 executor.setWaitForTasksToCompleteOnShutdown(true); return executor; }/** * 内部消息总线 */@Service@EnableAsyncpublic class EventBus implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } public void add(ApplicationEvent event) { publisher.publishEvent(event); }}// 事件类public class UploadEvent extends ApplicationEvent { public UploadEvent(Object source) { super(source); }}public class ConvertEvent extends ApplicationEvent { public ConvertEvent(Object source) { super(source); }}// 监听类@Componentpublic class UploadListener { @EventListener @Async("eventThread") // 使用自定义的线程池 public void process(UploadEvent event) { }}@Componentpublic class ConvertListener { @EventListener @Async("eventThread") public void process(ConvertEvent event) { }}配置管理实现为了提高文件服务器的灵活性,对于转换逻辑可进行配置 。如果没有进行相应的配置,则不会进行对应的处理 。
下面的四个类是对各个文件类型的配置:
- ImageConfig:切图大小
- OfficeConfig:转换类型,是否获取页码
- PdfConfig:转换类型,是否获取页码
- VideoConfig:转换类型,是否获取长度,是否取帧
- ImageConfigRespository
- OfficeConfigRespository
- PdfConfigRespository
- VideoConfigRespository
@Configuration@ConfigurationProperties(prefix = "fileupload.config")public class VideoConfigRespository { private List<VideoConfig> videoConfigList; /** * 根据分组(系统)找到对应的视频配置 * * @param group * @return */ public List<VideoConfig> find(String group) { if (videoConfigList == null) { return new ArrayList<>(); } else { return videoConfigList.stream().filter(it -> it.getGroup().equals(group)).collect(Collectors.toList()); } } public List<VideoConfig> getVideoConfigList() { return videoConfigList; } public void setVideoConfigList(List<VideoConfig> videoConfigList) { this.videoConfigList = videoConfigList; }}通过Spring的ConfigurationProperties注解,将属性文件中的属性配置到videoConfigList中 。
# 视频配置fileupload.config.videoConfigList[0].group=GROUP1 # 默认配置fileupload.config.videoConfigList[1].group=GROUP2fileupload.config.videoConfigList[1].type=webm# 转换为webmfileupload.config.videoConfigList[1].frameSecondList[0]=3 # 取第3秒的图片转换结果实现转换结果通过ConvertResult和ConvertFileInfo表示:
- ConvertResult中包含了源文件信息,以及多个转换结果 。ConvertFileInfo表示一个转换结果
- ConvertResult是Entity而ConvertFileInfo是VO
- ConvertResult与ConvertFileInfo是一对多的关系
- 两者构成聚合,其中ConvertResult是聚合根(关于聚合与聚合根请参考领域设计:聚合与聚合根)
@Componentpublic class ConvertResultRespository { ...... /** * 保存转换结果 * * @param result * @return */ public void save(ConvertResult result) { Path savePath = Paths.get(tokenPath, result.getToken()); try { if(!Files.exists(savePath.getParent())) { Files.createDirectories(savePath.getParent()); } Files.write(savePath, gson.toJson(result).getBytes(UTF8_CHARSET)); } catch (IOException e) { logger.error("save ConvertResult[{}} error!", result, e); } } /** * 查找转换结果 * * @param token * @return */ public ConvertResult find(String token) { Path findPath = Paths.get(tokenPath, token); try { if (Files.exists(findPath)) { String result = new String(Files.readAllBytes(findPath), UTF8_CHARSET); return gson.fromJson(result, ConvertResult.class); } } catch (IOException e) { logger.error("find ConvertResult by token[{}} error!", token, e); } return null; }}
推荐阅读
- 如何设计一个高并发系统?
- 大白话告诉你Hadoop架构原理
- 华为手机这些英文文件夹到底是啥?为什么我删完瞬间多出10个G
- 从微信小程序开发者工具源码看小程序架构设计实现原理
- 怎么把电脑文件无线批量传输到iphone,不压缩不用插线,很方便
- 普通家庭装修中走廊如何设计
- 电子杂志制作工具 电子杂志设计
- 如何一步步构建大型网站架构
- 架构图解:支付宝钱包系统架构内部剖析
- 数据库软件架构,到底要设计些什么?