文件上传-队列多线程读取文件内容
来源:互联网 发布:json遍历放到input中 编辑:程序博客网 时间:2024/05/10 17:42
队列多线程读取文件内容
为什么采用多线程队列的方式呢?主要是如果文件一行一行读取处理的话,页面上传文件后,需要等待服务器响应。如果文件的内容数据量很大的话,页面就一直等待服务器响应,毕竟服务器处理这些数据是花费很多时间的,我们都知道,页面是有响应时间的,一旦响应超时了,程序就终止了。所以,当我们需要处理大批的文件时,可以采用队列多线程的方式异步去处理,页面的响应时间只是上传文件的时间,一般在几秒之内吧。文件的数据一次性放到队列中,多个线程异步同时处理队列中的数据。
1. 场景:文件大小不是很大(十几兆以内),读取文件的内容。
方案:文件直接放到内存中,一行一行读取处理
在这里页面上传的我采用的是ajaxFileUpload插件,异步请求后台。当然,也可以用form表单的形式。
页面:
<input type="text"name="name"/> <input id="fileToUpload"type="file" name="file"onchange="selectFile(this);" style="margin-left: 50px;"/> <button onclick="ajaxFileUpload()">上传</button> <script type="text/javascript"> functionajaxFileUpload() { varfile = $("input[name='file']").val(); if(typeof(file)=="undefined"||$.trim(file).length==0) { alert("请上传文件!"); return; } $.ajaxFileUpload({ url : '/upload', type: 'post', secureuri : false, fileElementId : 'fileToUpload',//input 的id dataType : 'json', data : { "name": name//这里随便传递了name,毕竟我们上传文件,还需要其他的一些数据 }, success : function(data) { //这里根据后台的返回数据进行处理 }, error : function(data, status, e) { alert("上传发生异常"); } }) } // 判断上传文件类型,该处设置的只允许上传txt的文件 functionselectFile(file) { varfileName = file.value; varmime = fileName.toLowerCase().substr(fileName.lastIndexOf(".")); if(mime!=".txt"){ alert("该文件不是txt文件,请重新选择!"); file.outerHTML= file.outerHTML; } }</script>
后台:Controller
@SuppressWarnings("unchecked")@ResponseBody @RequestMapping(value = "/upload", method =RequestMethod.POST) public voidfileUpload(HttpServletRequest request, HttpServletResponse response) throwsException{ Map<String,Object> result = newHashMap<String,Object>(); result.put("success", false); //使用Apache文件上传组件处理文件上传步骤: //1、创建一个DiskFileItemFactory工厂 DiskFileItemFactoryfactory = new DiskFileItemFactory(); //2、创建一个文件上传解析器 ServletFileUploadupload = new ServletFileUpload(factory); upload.setSizeMax(50*1024*1024);//设置该次上传最大值为50M List<FileItem>list = upload.parseRequest(request); //注意:由于输入流只能读取一次,这里声明了两个文件流,inputStream用作文件读取处理,inputStreamCount用作计算文件行数,动态定义队列大小 InputStreaminputStream = null; InputStreaminputStreamCount = null; Stringname = null; for(FileItemfileItem : list){ //判断某项是否是普通的表单类型,否则该表单项是file 类型的 if(fileItem.isFormField()){ if(fileItem.getFieldName().equals("name")){ name= fileItem.getString("UTF-8"); } }else{ //用作-统计文件行数 inputStreamCount= fileItem.getInputStream(); inputStream= fileItem.getInputStream(); break; } } if(inputStream== null){ log.error("文件为空!"); result.put("errorMsg","文件为空!"); ResponseUtil.writeGson(response,result); return; } BufferedReader readerLineCount =new BufferedReader(newInputStreamReader(inputStreamCount)); //声明一个记录文件行数的变量 int lineCount = 0; while(readerLineCount.readLine() !=null)//获取文件行数 { lineCount++; } readerLineCount.close(); System.out.println("文件行数:"+lineCount); //动态创建队列 int DEFAULT_IO_THREADS = 10; BlockingQueue<Runnable> queue = newArrayBlockingQueue<Runnable>(lineCount); ThreadPoolExecutor executor = newThreadPoolExecutor(DEFAULT_IO_THREADS, DEFAULT_IO_THREADS*2,1, TimeUnit.HOURS, queue, new ThreadPoolExecutor.CallerRunsPolicy()); String line = null; int i =0; BufferedReader reader =newBufferedReader(new InputStreamReader(inputStream)); while(StringUtils.isNotEmpty(line = reader.readLine())) { //处理每一行的数据 send(executor,line); i++; } result.put("count",i); result.put("success", true); reader.close(); ResponseUtil.writeGson(response, result); } @SuppressWarnings("unused") privatevoid send(ThreadPoolExecutor executor, String line) { final String final_ String line=line; executor.submit(newRunnable() { publicvoid run() { //在这里写读取到的每一行的处理逻辑 } });}
2. 场景:文件大(几十兆甚至更大),读取文件的内容。
方案:文件不能直接放到内存中,会消耗大量的内存。采用上传至服务器临时文件夹中,从文件中一行一行读取处理
页面跟例子1是一样的,主要是后台的处理方式不一样
后台:Controller
使用MultipartFilem需要配置
<!-- 处理文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8" /> <!-- 编码 --> <property name="maxInMemorySize" value="10240" /> <!-- 上传时占用最大内存大小 (10240) --> <property name="uploadTempDir" value="/uploadTemp/" /> <!-- 上传临时保存目录名 ,带文件上传完成之后会自动删除保存的文件--> <property name="maxUploadSize" value="-1" /> <!-- 最大文件大小,-1为无限止(-1) --> </bean>
@ResponseBody @RequestMapping(value = "/upload", method =RequestMethod.POST) public void fileUpload(@RequestParam("file") MultipartFilemultipartFile ,HttpServletRequest request, HttpServletResponse response) throwsException{ Date startTime = new Date(); Map<String,Object> result =new HashMap<String,Object>(); result.put("success",false); if(multipartFile.isEmpty()){ log.error("文件为空!"); result.put("errorMsg","文件为空!"); ResponseUtil.writeGson(response,result); return; } String name =request.getParameter("name");//这种方式下,参数可以直接这么取 // 获取上传的文件保存的路径 String path = request.getSession().getServletContext().getRealPath("uploadTemp"); //如果目录不存在,创建文件夹 if (!new File(path).exists()){ newFile(path).mkdirs(); } // 获取上传的文件的名称 String filename =multipartFile.getOriginalFilename(); //文件上传至指定目录下 File targetFile = new File(path,filename); multipartFile.transferTo(targetFile);//上传 //计算文件行数(字符流 速度最快) //声明一个BufferReader对象 BufferedReader bReader = null; //用字符流对象实例化声明的BufferReader对象 bReader = new BufferedReader(newFileReader(targetFile)); //声明一个记录文件行数的变量 int lineCount = 0; //声明一个保存文件每行数据的String变量 while(bReader.readLine() != null)//获取文件行数 { lineCount++; } System.out.println("文件行数:"+lineCount); bReader.close(); //进行字符串与浮点类型数据转换时,先关闭刚操作的文件 int DEFAULT_IO_THREADS = 10; BlockingQueue<Runnable> queue= new ArrayBlockingQueue<Runnable>(lineCount); ThreadPoolExecutor executor = newThreadPoolExecutor(DEFAULT_IO_THREADS, DEFAULT_IO_THREADS*2,1, TimeUnit.HOURS, queue, new ThreadPoolExecutor.CallerRunsPolicy()); //通过行迭代,遍历文件的每一行,而不是把所有行都放在内存中 ,处理完之后把它扔掉。 LineIterator it =FileUtils.lineIterator(targetFile, "UTF-8"); int i=0; try { while (it.hasNext()) { String line = it.nextLine(); long userId = Long.valueOf(line); send(executor,line); i++; } result.put("count", i); result.put("success", true); Date endTime = new Date(); System.out.println("耗时:"+(endTime.getTime()-startTime.getTime())+"毫秒"); //删除临时文件 LineIterator.closeQuietly(it); targetFile.delete(); ResponseUtil.writeGson(response, result); } catch (Exception e){ result.put("errorMsg",e.getMessage()); ResponseUtil.writeGson(response,result); } finally { LineIterator.closeQuietly(it); } } @SuppressWarnings("unused") privatevoid send(ThreadPoolExecutor executor,String line) { final String line = line; executor.submit(newRunnable() { publicvoid run() { //在这里写处理逻辑 } }); }
补充:像这种文件上传的,一定要防止重复提交。所以,防重复提交必须防止。
方案: 1.前端,js点击提交后,立刻移除点击事件。
2.后端,加锁。private Map<String, String> objLock = new ConcurrentHashMap<String, String>();
1 0
- 文件上传-队列多线程读取文件内容
- 读取上传文件的内容
- java-文件上传到读取文件内容
- 直接读取上传文件的内容
- js读取上传的文件内容
- 【函数调用】上传文件并读取文件内容至内表
- Java上传excel文件并读取文件内容
- 读取spring的文件上传excel文件内容(CommonsMultipartFile)
- FileUpload上传文件及读取 .ini 文件内容
- 不经保存,直接读取上传文件的内容
- 不保存直接读取上传文件的内容!
- 不经保存,直接读取上传文件的内容
- 不经保存,直接读取上传文件的内容
- 不经保存,直接读取上传文件的内容
- 不经保存,直接读取上传文件的内容
- 不经保存,直接读取上传文件的内容
- 不经保存,直接读取上传文件的内容
- 不经保存,直接读取上传文件的内容
- SpringMVC单文件上传与多文件上传
- 全局钩子无法回调到回调函数中
- Android之MVP与MVC、MVVM的区别
- POJ 2478 Farey Sequence
- 群赛 round#5 解题报告(superoxide,choice,rpwt)
- 文件上传-队列多线程读取文件内容
- python 操作MySQL
- CSS 画三角形
- mongoose入门(二)
- Qt之输出控制
- 20160822关于阿里云的RDS说明总结
- 【poj1007】 DNA Sorting
- POJ 1964 City Game
- socket.io