100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【文件上传】接口优化之多文件多线程异步上传

【文件上传】接口优化之多文件多线程异步上传

时间:2019-02-22 11:57:44

相关推荐

【文件上传】接口优化之多文件多线程异步上传

文章目录

♥文件上传 接口优化♥一、😀问题(1)、💣发表图文时,上传接口处理时间长,导致用户前端页面一直卡着转圈。(2)、💣springboot中多线程中使用MultipartFile进行异步操作报错,系统找不到指定的文件(报错)。 二、🔧分析问题三、👍解决问题四、🐂核心代码1、核心代码文件展示2、自定义线程池配置3、异步任务管理器4、异步工厂(任务产生处)❓为什么使用文件流?5、确保后台退出时,关闭应用线程6、接口层代码

♥文件上传 接口优化♥

一、😀问题

(1)、💣发表图文时,上传接口处理时间长,导致用户前端页面一直卡着转圈。

(2)、💣springboot中多线程中使用MultipartFile进行异步操作报错,系统找不到指定的文件(报错)。

二、🔧分析问题

发表图文时,上传接口处理时间长,导致用户前端页面一直卡着转圈:因为接口是同步的,也就是用户上传文件的时候需要等文件上传完毕才会返回结果,当较多人同时或者上传较大文件时候就容易卡死。

三、👍解决问题

❀使用多线程异步上传❀: 用户立马就能获取上传响应结果(此时不包括上传成功与否),文件通过流的形式再后台多线程异步上传,这样缺点也就暴露出来了,也就是用户并不知道文件是否上传成功,对于新产生的这个问题的解决办法也很简单:1、轮询查询上传状态 2、在上传完成逻辑中使用websocket或其他方式向用户推送上传状态;3、没有必要关心上传状态等等;

四、🐂核心代码

项目是基于SpringBoot的向七牛云上传文件的服务。

1、核心代码文件展示

2、自定义线程池配置

package cn.xhubbq.config;import cn.xhubbq.utils.Threads;import mons.lang3.concurrent.BasicThreadFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.ThreadPoolExecutor;/*** 线程池配置** @author 甲粒子**/@Configurationpublic class ThreadPoolConfig{// 核心线程池大小private int corePoolSize = 50;// 最大可创建的线程数private int maxPoolSize = 200;// 队列最大长度private int queueCapacity = 1000;// 线程池维护线程所允许的空闲时间private int keepAliveSeconds = 300;@Bean(name = "threadPoolTaskExecutor")public ThreadPoolTaskExecutor threadPoolTaskExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setMaxPoolSize(maxPoolSize);executor.setCorePoolSize(corePoolSize);executor.setQueueCapacity(queueCapacity);executor.setKeepAliveSeconds(keepAliveSeconds);// 线程池对拒绝任务(无线程可用)的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;}/*** 执行周期性或定时任务*/@Bean(name = "scheduledExecutorService")protected ScheduledExecutorService scheduledExecutorService(){return new ScheduledThreadPoolExecutor(corePoolSize,new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),new ThreadPoolExecutor.CallerRunsPolicy()){@Overrideprotected void afterExecute(Runnable r, Throwable t){super.afterExecute(r, t);Threads.printException(r, t);}};}}

3、异步任务管理器

package cn.xhubbq.manager;import cn.xhubbq.utils.SpringUtils;import cn.xhubbq.utils.Threads;import java.util.TimerTask;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/*** 异步任务管理器* * @author 甲粒子*/public class AsyncManager{/*** 操作延迟10毫秒*/private final int OPERATE_DELAY_TIME = 10;/*** 异步操作任务调度线程池*/private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");/*** 单例模式*/private AsyncManager(){}private static AsyncManager me = new AsyncManager();public static AsyncManager me(){return me;}/*** 执行任务* * @param task 任务*/public void execute(TimerTask task){executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);}/*** 停止任务线程池*/public void shutdown(){Threads.shutdownAndAwaitTermination(executor);}}

4、异步工厂(任务产生处)

QiniuUtil.qiniu2即是封装的将文件上传到七牛云的工具:

public static Map<String,Object> qiniu2(List<InputStream> files){Configuration configuration=new Configuration(Region.region2());UploadManager uploadManager=new UploadManager(configuration);String key=null;//默认不指定key的情况下,以文件内容的hash值作为文件名String imgs="";int count=0;Map<String,Object> map = new HashMap<>();byte [] buffer = new byte[1024*1024*2];// 单个文件不超过2MBtry{Auth auth=Auth.create(AK,SK);String upToken=auth.uploadToken("xhubbq");Response response;for (InputStream file : files) {while ( file.read(buffer,0,buffer.length) != -1 ) {response=uploadManager.put(buffer,key,upToken);DefaultPutRet putRet = JSONObject.parseObject(response.bodyString(), DefaultPutRet.class);imgs +=(URL_PREFIX+putRet.key+"|");//注意最后多一个"|"++count;}// 关闭 流 防止内存溢出 ==== xxx ===file.close();}map.put("state",1);map.put("nums",count);map.put("imgs",imgs);return map;}catch (Exception e){e.printStackTrace();map.put("state",0);map.put("nums",count);map.put("imgs",imgs);return map;}}

※※ 注意其中的关闭流的代码,因为流是做为参数传入的,一定要关闭流,否则会导致内存泄漏。

❓为什么使用文件流?

首先前端传递过来的文件,会存储到临时文件夹中,即类似这样的一个路径。C:\Users\DELL\AppData\Local\Temp\tomcat.8180.459485606774542746\work\Tomcat\localhost\ROOT\upload_4330f731_2ae0_4955_a484_5862fd4530a2_00000009.tmp (系统找不到指定的文件。)

原因分析

异步执行的时候,主线程结束,临时文件就会被清空了,所以会报错。

解决方案

在开启异步之前,需要把MultipartFile(file)转换为流来进行操作即可file.getInputStream()

【参数说明】:其中AK,SK是七牛云的配置,返回的是上传成功的图片地址和数量,多地址用 | 线隔开的。

👇Post.BBQ代表是表白墙类型,其他的如许愿墙,失物招领等

package cn.xhubbq.manager.factory;import cn.xhubbq.pojo.post.*;import cn.xhubbq.service.post.IPostService;import cn.xhubbq.service.xguser.IUserService;import cn.xhubbq.utils.QiniuUtil;import cn.xhubbq.utils.SpringUtils;import com.alibaba.fastjson.JSONObject;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.multipart.MultipartHttpServletRequest;import javax.servlet.http.HttpServletRequest;import java.io.InputStream;import java.util.List;import java.util.Map;import java.util.TimerTask;/*** 异步工厂(产生任务用)** @author*/public class AsyncFactory{private static IUserService userService = SpringUtils.getBean(IUserService.class);private static IPostService postService = SpringUtils.getBean(IPostService.class);private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");/*** 测试* @return 任务task*/public static TimerTask posts( String content, int type, int nums, List<InputStream> inputStreams){return new TimerTask(){@Overridepublic void run(){if (content!=null){boolean b = userService.checkContentAndImages(null, content);if(!b){return;}Map<String, Object> qiniu = QiniuUtil.qiniu2(inputStreams);if( (int)qiniu.get("state") == 0 ){return;}if( (int)qiniu.get("nums") != nums ){return;}String imgs = (String)qiniu.get("imgs");switch (type){case Post.TYPE_BBQ:{Bbq bbq = JSONObject.parseObject(content, Bbq.class);bbq.setImgPath(imgs);postService.savePostInMongoDb(bbq);}case Post.TYPE_XYQ:{Xyq xyq = JSONObject.parseObject(content, Xyq.class);xyq.setImgPath(imgs);postService.savePostInMongoDb(xyq);}case Post.TYPE_SWZL:{Swzl swzl = JSONObject.parseObject(content, Swzl.class);swzl.setImgPath(imgs);postService.savePostInMongoDb(swzl);}case Post.TYPE_TZSC:{Tzsc tzsc = JSONObject.parseObject(content, Tzsc.class);tzsc.setImgPath(imgs);postService.savePostInMongoDb(tzsc);}}}}};}}

5、确保后台退出时,关闭应用线程

package cn.xhubbq.manager;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.ponent;import javax.annotation.PreDestroy;/*** 确保应用退出时能关闭后台线程** @author 甲粒子*/@Componentpublic class ShutdownManager{private static final Logger logger = LoggerFactory.getLogger("sys-user");@PreDestroypublic void destroy(){shutdownAsyncManager();}/*** 停止异步执行任务*/private void shutdownAsyncManager(){try{logger.info("====关闭后台任务任务线程池====");AsyncManager.me().shutdown();}catch (Exception e){logger.error(e.getMessage(), e);}}}

6、接口层代码

@ResponseBody@RequestMapping(value = "/post/v2/{content}/{type}/{nums}",produces = "application/json;charset=UTF-8",method = RequestMethod.POST)public String postsV2(@PathVariable String content, @PathVariable int type, @PathVariable int nums, HttpServletRequest request) {Map<String, Object> map = new HashMap<>();//上传文件List<MultipartFile> files;List<InputStream> inputStreams = new ArrayList<>();MultipartHttpServletRequest req = (MultipartHttpServletRequest) request;files = req.getFiles("imgs");for (MultipartFile file : files) {try {inputStreams.add(file.getInputStream());} catch (IOException e) {e.printStackTrace();}}AsyncManager.me().execute(AsyncFactory.posts(content,type,nums,inputStreams));map.put("state", 1);return JSONObject.toJSONString(map);}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。